Skip to content

FLAG_SUBSCRIPTION took envelope bit 7, which every decoder before it rejects — was a tolerance phase intended? #105

Description

@iceteaSA

Found auditing the 0.17.39 → 0.18.2 wire delta before deploying. Inert today — nothing is broken and no action is needed for the current fleet. Filing because the hazard arrives with the first consumer that uses the flag, and by then the ordering is fixed.

What changed

5567732d ("mason: implement drain triad protocol") allocated envelope bit 7:

pub const FLAG_SUBSCRIPTION: u8 = 0b1000_0000;

Before that commit, bit 7 was a reserved-bit tripwire. The decoder's own doc comment at 5567732d~1:

/// A reserved flag bit (6-7) is set.
ReservedFlagBits { flags: u8 },

So any decoder built before this allocation rejects a frame carrying bit 7, rather than ignoring the bit it doesn't know.

Why it is inert right now

FLAG_SUBSCRIPTION appears zero times in crates/subc-core/src. The daemon never sets it — the doc comment describes it as client-declared ("a request credit the client explicitly declares as a held-open subscription"). A module therefore never receives it from the daemon, and no client currently speaks 0.21.0.

Live census on this box after deploying 0.18.2 — all four modules healthy, all behind the allocation:

MODULE          WIRE     daemon: 0.21.0
aft             0.13.0
claustrum       0.19.0
insula          0.19.1
magic-context   0.19.1

The ordering hazard

The first client built against 0.21.0 that sets FLAG_SUBSCRIPTION on a request routed to a module still on ≤0.20.0 will have that frame rejected by the module, not refused by the daemon. The splice is opaque by design: it rewrites channel and epoch and forwards the flags byte as-is, so it will faithfully deliver a bit the destination is built to treat as a protocol violation.

The failure surfaces at the far end, in a module whose author never opted into anything, as a decode error on a frame the daemon considered well-formed.

This is the same shape DAEMON_ORIGIN needed on bit 6, and it was handled there in two phases — #6 / #61: every decoder accepts the bit (Phase 1) before anything emits it (Phase 2), with a fleet census to confirm Phase 1 had actually landed everywhere. Phase 1 for bit 6 shipped as its own PR precisely so the emission change could not outrun it.

I can't find the equivalent for bit 7. Three possibilities and I can't distinguish them from the outside:

  1. The tolerance phase shipped somewhere I didn't find — in which case, point me at it and I'll close this.
  2. It's deliberate, because FLAG_SUBSCRIPTION is only ever set on a connection whose negotiated version already proves bit-7 tolerance. If so, that invariant is worth stating at the constant, since the next reader will reach for the DAEMON_ORIGIN precedent and expect a phase.
  3. It was missed — in which case a Phase 1 (decoders accept bit 7, nothing emits) wants to land, and reach the fleet, before any subscribing client ships.

Why bit 7 is the last one

Bits 0–7 are now fully allocated: binary, priority (1–2), last, admission (4–5), DAEMON_ORIGIN, FLAG_SUBSCRIPTION. There is no reserved space left in the flags byte, so this is the final time this particular ordering question can be asked — and the first frame type that needs a new flag will need a different mechanism entirely.

No action needed today; the fleet is healthy and I'd rather ask early than find out from a module's decode error.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions