macula_group_keys (macula v13.4.0)
View SourceA sealed group's distributor (plans/DESIGN_E2E_SEALED_PUBSUB.md §3, §5): the handler of <org>/group_keys_v1 and the epochs of every group it serves.
The application advertises the handler as any provider advertises one (its mcl_om capability, or macula_response:advertise_direct/7 kept alive with reuse_sup), with advertise_opts/1 merged into its options: the policy {realm_member_required, OrgKeyId, <<"group_keys">>}, so the org's group_keys UCAN is checked by macula before the handler runs, matched to the org key by its key id, AND confidential => required, so the provider's link answers a clear call sealed_required and a key never travels clear. With the node's kem_advertise switched off those options refuse to advertise at all (kem_advertise_disabled), rather than advertise a keyless distributor. The member side refuses a keyless distributor too (macula_group_keyring pulls with confidential => required): each side enforces it on its own. The handler then decides from the caller's wire-authenticated node id: a node in the application's removed set is refused every epoch, and so is a node that is not a live realm member, read from the realm's slot for its endorsement (macula_record:realm_member_endorsement_key/2 and macula_hyparview_endorsement:slot_endorsement/4, which answers withdrawn for the realm's tombstone). Membership is checked at the time of the call, for a past epoch too: a subscriber catching up on one it missed is still a member, and one that is not has no business reading the group.
A call names the group (prefix) and the epoch it wants: current, or a past one by id. The reply carries the group's policy and the epochs: the current one, and the next one too inside the current's ahead window (macula_group_epoch). Epoch keys are kept in memory only. A refusal is {error, Reason}, which macula answers as a sealed provider error with code handler_error and the reason as its detail: not_a_member, membership_unknown (the lookup failed; retry), unknown_epoch, epoch_expired or unknown_group (a prefix this distributor's org does not own).
Summary
Functions
The options the distributor's procedure is advertised with, merged into the application's own: the org's grant, checked before the handler runs, and sealed calls only.
The <org>/group_keys_v1 handler for a distributor, to advertise. It waits for the distributor longer than a membership lookup can take (5 s) and less than a member's pull deadline (15 s), so a slow lookup answers membership_unknown, and no answer is made that no member still waits for.
Start a distributor for org's groups. membership defaults to the realm slot read over pool, which then needs realm, realm_key_id and profile; removed defaults to nobody removed. The application keeps its removed set across restarts: a set lost to a restart re-admits every removed member whose grant still verifies.
Types
-type options() :: #{org := binary(), policy := policy(), rotate_after_ms => pos_integer(), now => fun(() -> integer()), membership => fun((<<_:256>>) -> ok | {error, not_a_member | membership_unknown}), removed => fun((<<_:256>>) -> boolean()), pool => pid(), realm => <<_:256>>, realm_key_id => <<_:256>>, profile => macula_crypto_profile:profile()}.
-type policy() :: required | preferred | off.
Functions
-spec advertise_opts(<<_:256>>) -> #{auth := {realm_member_required, <<_:256>>, binary()}, confidential := required}.
The options the distributor's procedure is advertised with, merged into the application's own: the org's grant, checked before the handler runs, and sealed calls only.
The <org>/group_keys_v1 handler for a distributor, to advertise. It waits for the distributor longer than a membership lookup can take (5 s) and less than a member's pull deadline (15 s), so a slow lookup answers membership_unknown, and no answer is made that no member still waits for.
Start a distributor for org's groups. membership defaults to the realm slot read over pool, which then needs realm, realm_key_id and profile; removed defaults to nobody removed. The application keeps its removed set across restarts: a set lost to a restart re-admits every removed member whose grant still verifies.