Sparkplug B Properly: Birth and Death Certificates, Metrics and Rebirth
The first message the carton transfer gateway sends is 592 bytes on the topic spBv1.0/Plant2_CartonLine/NBIRTH/GW-TR14, it carries all 19 metrics the node will ever publish, and it goes out at QoS 0 with the retain flag clear. Everything about that sentence looks wrong to somebody who has run plain MQTT, and every part of it is required by the specification.
The reason is the message that was registered before it, in the CONNECT packet, and never sent.
Sparkplug B is a state machine that happens to travel over MQTT, and the sparkplug b birth and death certificates are the whole of the state machine. The birth says here is everything I have and here is what it is worth right now. The death, which the broker publishes on the node’s behalf when the node cannot, says none of that can be trusted any more. Between them a subscriber always knows which of two very different things it is looking at: a value of zero that a machine genuinely reported, or a value of zero left on the screen by a publisher that stopped existing forty seconds ago.

The order is the point. The subscription at step two exists so that the node can answer a rebirth request from the moment anybody can see it, which is before it has published a single value.
What the birth certificate has to contain
Every metric the edge node will ever report on. Not the ones it happens to have now: the ones it will ever have. That phrase is in section 4.2.1 and it is the design constraint people discover late, because a metric that was not in the birth cannot be published in a data message and be understood, and adding one means a new birth for everybody. Each metric carries at minimum a name, a data type and a current value, and if templates are used, every template definition has to be in the birth as well. Two of those metrics are not process data at all. The first is bdSeq, an Int64 that must match the value in the will payload registered at connect time and must increment on every new CONNECT, wrapping from 255 back to zero. The second is a boolean called Node Control/Rebirth, which must be present, must be false, and must not be given an alias. That last rule reads like pedantry until you see the reason the specification gives: aliases are assigned in the birth, so a host application that has never seen this node’s birth would have no idea what alias to use, and the one command it needs to be able to send is precisely the one asking for that birth. Leaving the rebirth metric unaliased is what makes a cold host able to bootstrap itself. The rest of the birth is your machine: on the carton transfer, seven metrics per belt for the run bit, the fault bit, the fault code, the starts, the run seconds, the speed and the motor current, plus the guard relay mirror, the end-of-line eye and its count. Nineteen metrics with the two control metrics included, 592 bytes encoded, sent once per session.
After that, a changed metric costs 27 bytes if it goes by alias and 42 if it goes by name.
That fifteen-byte difference is why aliases exist, and on a metered link across forty nodes it is the difference between a plan that works and one that does not. The alias is bound to the name in the birth and used alone in every data message afterwards.
So a subscriber that missed the birth cannot read the data at all, which is the second reason rebirth exists.
The death certificate is registered before anything is published
The will goes in the CONNECT packet. Topic spBv1.0/Plant2_CartonLine/NDEATH/GW-TR14, QoS 1, retain false, and a payload containing exactly one metric: bdSeq. Nothing else. The specification is explicit that an NDEATH must not include a sequence number, and the reason is worth stating because it explains the whole design: the will payload is composed at connect time, held by the broker, and published possibly hours later by a process that has no idea what the edge node was doing when it stopped. It cannot carry a state. It can only carry an identity.

Every data and birth message is QoS 0. The two at QoS 1 are the will and the host’s own state topic, which are the two messages whose loss would leave somebody believing a lie.
That table is the answer to the most common objection, which is that publishing plant data at QoS 0 sounds reckless. The sequence number does the work a PUBACK would have done, and it does it better: a PUBACK tells the publisher its message reached the broker, while a gap in the sequence tells the consumer that something did not reach the consumer. Those are different facts and only the second one is useful to the person looking at the screen. The birth and death messages cannot rely on a sequence number in the same way, because a birth resets the count and a death has no count at all, so those are the ones that get the acknowledged delivery. The other flag that looks wrong is clean start. Every Sparkplug client, edge node and host alike, must set clean session true on MQTT 3.1.1, or clean start true with a session expiry interval of zero on MQTT 5.0. No queue is kept for a disconnected Sparkplug client anywhere on the broker. That is deliberate and it follows from the birth: a node that reconnects publishes a complete new birth with fresh values, so a queue of stale data messages from the previous session would be worse than useless. Buffering belongs at the edge, in the gateway, under the gateway’s own control, which is where the store and forward arithmetic has to be done.
A clean exit against a pulled cable
Here is the thing the specification is actually for.

The amber band is the number to take away. On a keep alive of 40 seconds the consumer spends a minute showing values it has every reason to believe and no reason to doubt.
A gateway shutting down on purpose publishes an NDEATH itself and then sends a DISCONNECT. The DISCONNECT deletes the stored will, so only one death message ever appears, and it appears immediately. Every subscriber marks the node offline within the round trip. The alternative path is the one that gets tested by an electrician rather than by you: the link drops, the gateway sends nothing, and the broker waits until one and a half times the keep alive interval has elapsed before publishing the will on the node’s behalf. On a keep alive of 40 seconds that is 60 seconds during which every dashboard, every historian and every alarm engine is showing the last values it received, with quality Good, with no indication of anything at all.
That window is a design parameter and almost nobody treats it as one.
Write it on the drawing as the keep alive multiplied by 1.5, agree it with whoever answers when a line stops, and set the keep alive from that number rather than from a default.
Thirty seconds of keep alive gives a 45-second worst case, which is defensible. Three hundred gives seven and a half minutes, which is not.
There is a subtlety in the clean path that catches implementers. A node is allowed to publish its NDEATH and then simply drop the connection without a DISCONNECT, in which case the broker later publishes the will as well and the host receives two deaths. The specification handles this with bdSeq: the second one carries a bdSeq the host has already seen and processed, so it must be ignored. That same rule covers the nastier ordering, where a will held from a previous session is delivered after the node has already reconnected and published a new birth. Without bdSeq the host would take a stale death message as current and mark a running machine offline. With it, the death carries bdSeq 7 while the current session is bdSeq 8, and the host knows to throw it away.
bdSeq is not a nicety. It is the only thing separating a correct host from one that flaps.
What happens on an NDEATH, exactly
The specification lists four things a host application must do the moment a death certificate arrives, and all four use the host’s own UTC clock rather than any timestamp in the message. Mark the edge node offline. Mark every metric that was in that node’s birth as stale. Mark every device under that node offline. Mark every metric from every one of those devices’ births as stale. The reason the host uses its own clock is the same reason the death carries no sequence number. A will is published by the broker, and the timestamp inside it was written when the CONNECT packet was composed, which might be nine hours earlier. It is meaningless as an event time. So Sparkplug requires that every participant keep a synchronised UTC clock and that every timestamp in the system be UTC, and a plant where the gateway clock and the host clock disagree by ninety seconds will produce a history where machines go offline before the events that stopped them. Getting that right across a cell is its own job, and time synchronisation with PTP, NTP and the PROFINET and EtherNet/IP mechanisms is where it gets settled. The clock drift that hides inside a data path is the same one described in the timestamps article, and Sparkplug is less forgiving of it than most systems because it stamps events rather than samples.
The sequence number, the wrap, and the rebirth request
Every NBIRTH, DBIRTH, NDATA and DDATA carries a seq that starts at 0 in the birth and increases by one with each message, wrapping from 255 to 0. A host that sees 1, 2 and then 4 knows it missed something.

Notice how long the node is silent. From the rebirth command to the new DBIRTH the machine is publishing nothing at all, because the specification requires data to stop until the whole birth sequence is out.
What it does about that is a reorder timeout, and the reason a timeout is needed rather than an immediate alarm is that messages on different topics can genuinely arrive out of order, especially through a clustered broker. So the host starts a timer when it sees the gap. If the missing message turns up before the timer expires, the timer is cancelled and nothing happens. If it does not, the host publishes an NCMD to spBv1.0/Plant2_CartonLine/NCMD/GW-TR14 containing the metric Node Control/Rebirth with a value of true, and the edge node must immediately stop sending data messages, publish a complete new NBIRTH and every DBIRTH, and only then resume. The bdSeq stays the same through all of this, because no new MQTT session was established, and a host that expects a new bdSeq on a rebirth will mark the node offline for no reason.
A couple of seconds is the usual reorder timeout. Zero is legitimate on a single broker that guarantees ordering.
The same rebirth mechanism answers a question that has nothing to do with lost messages: what a host application does when it connects to a broker where nodes have been running for a week. It subscribes, it receives a DDATA full of aliases, and it has no idea what any of them mean because it never saw the birth. Asking for a rebirth is the whole of the solution, and it is why every edge node is required to implement that one command whether or not it implements any other. A host that comes up and quietly shows nothing for an hour is almost always a host that never sent the request.
What the consumer actually sees
This is where the value is, and it is the thing plain MQTT cannot do without you building it.

Read rows two and three together. The number on the wall is identical and the meaning is opposite, and the only thing carrying the difference is a quality flag that a bare MQTT topic does not have.
A belt that has genuinely stopped publishes Running as false and the tag shows false with good quality. A belt that has been running steadily for an hour publishes nothing at all, by design, because report by exception is the whole model and the specification says data should not be published periodically. The tag shows the last value it received, with good quality, and that is correct. A gateway that has died shows the same last value with stale quality, and the difference between those two is the difference between an operator trusting a screen and an operator walking to the machine to check. Build the stale state into the display. A greyed-out value with a timestamp beats a value that looks current, and it costs nothing at the point where the host already knows. One more topic exists and it runs the other way. The host application publishes its own state to spBv1.0/STATE/
Next step
Take one gateway, one broker and one subscriber, and run three tests in an afternoon. Stop the gateway service cleanly and time how long until the subscriber shows stale. Pull its network cable and time the same thing, then compare that number against 1.5 times your keep alive. Then start a second subscriber while the first is still running, watch it receive data it cannot interpret, and confirm it sends a rebirth request rather than sitting there empty.
If the third test fails, the subscriber is the problem and no amount of gateway configuration will fix it. That is worth knowing before a plant depends on it, and the data logging and remote monitoring practices that sit on top of this layer all assume it has already been proved.