Skip to main content

Module roster

Module roster 

Source
Expand description

Folding fetched authority editions into the current roster.

The control plane is a set of per-entity append editions (§edition), each real-npub-signed and version-chained (§version). This module is the consumer: given the (already-decrypted) inner edition events, it verifies each authorship signature, groups editions per entity, folds each entity’s version chain to its current head, and deserializes the heads into the in-memory roles::CommunityRoles the authority gates query.

Two layers: fold_roster produces the “validly signed, anchored, bound, current” roster (the inner signature proves WHO authored each edition), and authorize_delegation then filters it by the delegation chain — deciding WHETHER each signer was allowed (rank + the chain to the owner), so a self-signed or forged-delegation entry never becomes trusted authority. Entities that come back with a chain gap (unanchored / withheld prereqs, §version) are reported so the caller can fail closed (tracking) or accept-via-authority (bootstrapping) per.

Structs§

ChannelMetaHead
A folded ChannelMetadata edition (vsk=2): the channel it addresses, its content, its signer (for the MANAGE_METADATA authority gate), and its version head (to advance set_edition_head).
EntityHead
The folded current head of one control entity — what the caller persists via set_edition_head (the monotonic refuse-downgrade floor) and the send side reads to emit the next version.
FoldedRoster
The outcome of folding the control plane.
InviteLinkSet
A folded per-creator InviteLinks edition (vsk=8): the creator who signed it, their active link locators, and the version head. Applied only if creator held CREATE_INVITE.
RootCandidate
One gap-vetted GroupRoot candidate (vsk=0): its content, signer (for the authority gate), and head. The consumer scans FoldedRoster::root_candidates (highest version first) for the highest whose author currently holds MANAGE_METADATA.

Functions§

authority_citation_satisfied
version-pinned authority completeness check for an action that carries an edition::AuthorityCitation (a ban, a hide, a delegated grant). The action names the authorizing grant edition it claims authority under; this confirms we have folded a COMPLETE, un-forked view of that grant — synced to AT LEAST the cited version, with the cited hash matching ours at equality.
authorize_delegation
Filter a fold_roster result by the delegation chain → the AUTHORIZED roster. Binding
build_banlist_edition
Build a signed Banlist edition (vsk=4) at the single community-wide coordinate (entity_id == banlist_locator(community_id) — community-scoped so it survives a base rotation and re-anchors). Content is the JSON array of banned pubkeys (lowercase hex). Signed by the actor’s real keys; the consumer (fold_roster + the BAN-authority check) decides whether that signer was allowed to ban.
build_banlist_edition_unsigned
The UNSIGNED Banlist edition (the bunker path); sign with the active NostrSigner.
build_channel_metadata_edition
Build a signed ChannelMetadata edition (vsk=2) at the channel’s coordinate (entity_id == channel_id) — the channel’s display descriptor (name). Real-npub signed; the consumer applies it only if the signer held MANAGE_CHANNELS.
build_channel_metadata_edition_unsigned
The UNSIGNED ChannelMetadata edition (the bunker path); sign with the active NostrSigner.
build_community_root_edition
Build a signed GroupRoot edition (vsk=0) at the community-wide coordinate (entity_id == community_id) — the Community’s display descriptor (name/description/icon/banner + owner attestation). Real-npub signed; the consumer applies it only if the signer held MANAGE_METADATA.
build_community_root_edition_unsigned
The UNSIGNED GroupRoot edition (the bunker path); sign with the active NostrSigner.
build_grant_edition
Build a signed Grant edition (vsk=3) at its bound coordinate (entity_id == grant_locator(community_id, member) — community-scoped so it survives a base rotation, the keystone for re-anchoring), the next version in that member’s grant chain. Signed by the actor’s real keys. An empty role_ids is a revoke (folds to no entry).
build_grant_edition_unsigned
The UNSIGNED Grant edition (the bunker path); sign with the active NostrSigner.
build_group_dissolved_edition
Build a signed GroupDissolved tombstone (vsk=10) at the community-scoped dissolved locator (entity_id == dissolved_locator(community_id) — rotation-stable so a post-rotation joiner still finds it). UNLIKE every other edition this has NO version chain and NO prev-hash: it is minted at a fixed version == 1 with no prev_hash and is exempt from check_chain_shape — presence of ≥1 valid OWNER-signed edition here IS dissolution (duplicates are idempotent). Content is minimal ({}); the signer (the owner) is the whole payload. Authority (the signer == proven owner) is the CALLER’s check.
build_group_dissolved_edition_unsigned
The UNSIGNED GroupDissolved tombstone (the bunker path — the owner may sign remotely); sign with the active NostrSigner. No chain discipline (see build_group_dissolved_edition).
build_invite_links_edition
Build a signed InviteLinks edition (vsk=8) at the CREATOR’s own coordinate (entity_id == invite_links_locator(community_id, actor)). Content is the JSON array of THAT creator’s active public-invite-link LOCATORS (lowercase hex; the locator is public — the token in the URL is the secret, never listed). Per-creator: a creator publishes only their own list; members fold every creator’s list (gated on the author holding CREATE_INVITE) into the aggregate active-set, the source of truth for the Public/Private mode + registry-authoritative joins. No shared registry.
build_invite_links_edition_unsigned
The UNSIGNED InviteLinks edition (the bunker path); sign with the active NostrSigner. The entity coordinate binds to author, so a creator can only publish links at their own coordinate.
build_role_edition
Build a signed RoleMetadata edition (vsk=1) at its bound coordinate (entity_id == role_id), the next version in the role’s chain. Signed by the ACTOR’s real keys (the authorship proof); the caller supplies the next version + the prior edition’s prev_hash (the held head’s self_hash, or None with version == 1 for a brand-new role — see [check_chain_shape]). The resulting inner event is what the envelope then seals under the server-root key for publication.
build_role_edition_unsigned
The UNSIGNED RoleMetadata edition (the bunker path): build the inner, then sign it with the active NostrSigner (unsigned.sign(&signer).await) so a NIP-46 remote signer works. The sync build_role_edition is the local-keys convenience over this.
control_pseudonym
The relay-filterable pseudonym members use to fetch the control plane for (community, epoch). Derived from the server-root key + community id, so members compute it but outsiders (no server-root) can’t — the control plane has no stable on-wire identifier. This reuses the channel-pseudonym derivation; what keeps a control pseudonym from ever aliasing a channel’s is the invariant that the server-root key is always distinct from every channel key (the HKDF label is shared, so domain separation rests on the distinct IKM + id32).
dissolved_tombstone_signer
Open a wire event at the dissolved coordinate and, IF it is a well-formed GroupDissolved tombstone (vsk=10 at dissolved_locator), return its inner real-npub signer. The CALLER decides validity by checking that signer equals the proven owner (fail-closed authority). None for anything else (wrong key, malformed, wrong vsk, relabelled entity_id).
fold_roster
Fold a set of (already-decrypted) inner edition events into the current roster.
open_control_edition
Open a control-edition outer → its inner edition event (decrypt under the server-root key). Does NOT verify the inner signature or parse fields — pass the result to edition::parse_edition_inner (which verifies authorship) and then fold_roster (which binds + folds). A wrong server-root key fails to decrypt → Err, which is also how cross-community replay is rejected.
seal_control_edition
Seal a signed control edition (kind 3308) for the wire: encrypt the inner under the server-root key (only members decrypt), with an ephemeral outer signer (the real author is the inner signature, hidden from relays), addressed by the control-plane pseudonym so members fetch by #z without exposing a stable group identifier.
seal_dissolved_edition
Seal a GroupDissolved tombstone for the wire at the ROTATION-STABLE coordinate: encrypt the inner under the community-id-derived dissolved_envelope_key (NOT the per-epoch server root) and address it by dissolved_pseudonym (NOT control_pseudonym), so it is discoverable + openable by any member or joiner at any epoch. Ephemeral outer signer (the owner is the inner signature). The tombstone is also published at the current control_pseudonym (a current-epoch fast path); this is the cross-epoch path that survives a concurrent re-founding.