gobridge

0009 — Durable outbound MQTT session state

Status: accepted Date: 2026-07-13 Deciders: GoBridge core Decision recorded: commit 4d8d76d (2026-07-13, this ADR file) Implementation: NonDurableEgressReporter, Sender.NonDurableEgress, and egressDurabilityAdvisory in commit 9d8effb (2026-07-10)

Context

The paho MQTT adapter connects through autopaho with cfg.Session left nil, so the connection manager uses autopaho’s default in-memory packet/session store (adapters/mqtt/transport/paho/acl_session.go, the deferred-alternative note on the autopaho.ClientConfig build). That store holds the client-side outbound queue: a QoS 1/2 PUBLISH that has been sent but whose PUBACK (QoS 1) / PUBCOMP (QoS 2) has not yet arrived lives only in process memory.

The consequence is a hard ceiling: an in-flight outbound QoS 1/2 publish is lost at the MQTT-protocol level when the process dies, and MQTT QoS 2 is therefore not exactly-once across a restart. client_id / clean_start=false do not close this — they resume broker-side session state (offline inbound queueing, subscription retention), not the client-side outbound packet queue, which is the volatile part.

This is easy to mistake for bridge-level message loss. It is not, because the bridge does not delegate egress durability to the MQTT protocol.

Decision

Keep the in-memory autopaho store and make durable, at-least-once egress the bridge’s responsibility at the route layer, not the transport’s. The MQTT sender is a non-durable boundary by design and it declares that fact so the runtime can reason about it.

Consequences

Rejected alternatives