Stable Conduit

stable

A StableConduit wraps a link and provides automatic reconnection with replay of missed items. It implements the Conduit interface: the session layer above it sees a reliable, ordered stream of items, even when the underlying link fails and is replaced.

StableConduit is entered only after the transport prologue has accepted the stable conduit mode. The stable-conduit handshake specified on this page is therefore not the first exchange on a fresh link attachment.

StableConduit continuity is below the RPC retry layer. It preserves conduit items; it does not by itself define when an RPC should be retried, resumed, or replayed as the same logical operation. See Retry.

Handshake

stable.handshake

Every stable-conduit attachment begins with a stable handshake after the transport prologue has accepted stable. The client sends a ClientHello, the server responds with a ServerHello. These messages are postcard-encoded and sent as raw link payloads (not wrapped in stable frames).

stable.handshake.client-hello

ClientHello carries:

  • resume_keyOption<ResumeKey>. None for a fresh session; Some(key) to resume a previous session.
  • last_receivedOption<PacketSeq>. The highest sequence number the client has successfully received. None if no items have been received yet.
stable.handshake.server-hello

ServerHello carries:

  • resume_key — a freshly generated ResumeKey for this session. The client MUST use this key in subsequent reconnection attempts.
  • last_receivedOption<PacketSeq>. The highest sequence number the server has successfully received. None if no items have been received yet.
stable.resume-key

A resume key is an opaque byte buffer generated by the server. It MUST be at least 16 bytes and SHOULD be generated using a cryptographically secure random source. The server uses it to identify which session a reconnecting client belongs to.

Framing

stable.framing

After the handshake, all traffic is wrapped in frames. Each frame carries:

  • seq — a PacketSeq (u32) assigned by the sender
  • ack — an optional PacketAck piggybacked by the sender
  • item — the conduit-level item (e.g. a session Message)
stable.framing.encoding

On the wire, a frame is encoded as a postcard-serialized FrameHeader (seq + ack) concatenated with the separately postcard-serialized item bytes. The replay buffer stores the complete encoded frame bytes (header + item). On replay, frames are retransmitted with their original sequence numbers preserved.

Sequencing

stable.seq

Each direction has an independent sequence counter. Sequence numbers start at 0 and increment by 1 for each frame sent, wrapping at u32 boundaries.

stable.seq.monotonic

Within a single link, sequence numbers MUST be strictly increasing (modulo u32 wrap). A receiver that observes a sequence number less than or equal to its last received sequence (accounting for wrap) MUST ignore the duplicate frame.

Acknowledgment

stable.ack

Acknowledgments are piggybacked on data frames. A frame's ack field, when present, contains a PacketAck with a max_delivered field: the highest sequence number the sender of the ack has successfully received and processed.

stable.ack.trim

Upon receiving an ack, the sender MUST trim its replay buffer: all frames with sequence numbers less than or equal to max_delivered can be discarded, as the peer has confirmed receipt.

Replay buffer

stable.replay-buffer

The sender MUST retain the encoded frame bytes (header + item) in a replay buffer until the peer acknowledges receipt via an ack. On reconnection, unacknowledged frames are retransmitted with their original sequence numbers.

stable.replay-buffer.order

Replayed items MUST be sent in their original sequence order before any new items are sent on the fresh link.

Reconnection

stable.reconnect

When a link fails, the StableConduit obtains a new link from its LinkSource and performs a new handshake. The client sends a ClientHello with the resume_key from the previous ServerHello and its last_received sequence number.

stable.reconnect.server-replay

Upon receiving a ClientHello with a valid resume_key, the server identifies the session, determines which items the client missed (those with sequence numbers greater than last_received), and replays them on the new link before sending any new items.

stable.reconnect.client-replay

Similarly, the client uses the server's last_received from the ServerHello to replay any items the server missed.

stable.reconnect.failure

If the server does not recognize the resume_key (e.g. the session expired or the server restarted), it MUST reject the resume attempt. The client MUST treat this as a session loss and surface an error to the layers above.

What happens after that session loss is governed by Retry: stable reconnection stops at conduit continuity, while session continuity, operation-level retry, and caller-visible failure outcomes are defined separately there.