macula_hyparview_proto (macula v10.8.0)
View SourceHyParView protocol orchestrator (Part 3 §7.1).
Pure functional layer on top of macula_hyparview_view. Given the current view, an incoming HyParView frame, and a context (self NodeId, realm, signing identity, ARWL/PRWL constants), process/4 returns the updated view plus a list of {send, TargetPeer, Frame} actions the wrapping process should transmit.
Message handlers
- JOIN — receiver becomes the joiner's contact: add_active(joiner), reply NEIGHBOR(high), FORWARD_JOIN(ttl=ARWL) to every other active peer. Eviction in the active view emits DISCONNECT to the demoted peer. When
ctx()carriesrealm_admin_pubkey, admission is gated on the frame'srecordfield holding arealm_member_endorsementsigned by that key for this exact (realm, joiner) pair (macula_hyparview_endorsement: verify_endorsement/3) — a missing or invalid endorsement is dropped silently (no ack, no forward, view unchanged), never admitted. Withoutrealm_admin_pubkeyinctx(), admission is unconditional (opt-in gating, matches the frame spec's own "endorsement is optional" design). - FORWARD_JOIN — if ttl=0 or active view is empty: add_active(new_member) + reply NEIGHBOR(high). Otherwise: if ttl == PRWL, also add_passive(new_member); decrement ttl and forward to a random active peer that is not the sender. Carries the ORIGINAL JOIN's
record(endorsement) through the whole forward chain and re-verifies it at every admission point — trust in the endorsement is never transitively assumed just because a neighbour forwarded it. - NEIGHBOR(high) — always add_active(sender), evicting if needed.
- NEIGHBOR(low) — add_active(sender) only when the active view has room.
- DISCONNECT — demote sender to passive.
- SHUFFLE — if ttl > 0 and a forwardable neighbour exists, decrement ttl + forward; otherwise build SHUFFLE_REPLY against our own sample and send back to the origin, then merge the incoming sample into our passive view.
- SHUFFLE_REPLY — merge the incoming sample into the passive view.
Reference: plans/PLAN_MACULA_V2_PART3_DISCOVERY.md §7.1; plans/PLAN_PHASE_5_BREAKDOWN.md Session 5.2.
Summary
Functions
Build a signed JOIN frame to send to a contact peer.
Build a signed SHUFFLE frame for a periodic shuffle round. The orchestrator picks a random active neighbour to send it to.
Types
-type action() :: {send, peer(), macula_frame:frame()}.
-type ctx() :: #{self_id := peer(), realm := <<_:256>>, identity := macula_identity:key_pair(), arwl => non_neg_integer(), prwl => non_neg_integer(), shuffle_ttl => non_neg_integer(), shuffle_active_size => non_neg_integer(), shuffle_passive_size => non_neg_integer(), realm_admin_pubkey => macula_identity:pubkey(), self_endorsement => macula_record:m_record()}.
-type peer() :: macula_identity:pubkey().
Functions
-spec build_join(ctx()) -> macula_frame:frame().
Build a signed JOIN frame to send to a contact peer.
-spec build_shuffle(ctx()) -> {ok, macula_frame:frame()}.
Build a signed SHUFFLE frame for a periodic shuffle round. The orchestrator picks a random active neighbour to send it to.
-spec process(macula_hyparview_view:view(), peer(), macula_frame:frame(), ctx()) -> {macula_hyparview_view:view(), [action()]}.