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§
- Channel
Meta Head - A folded ChannelMetadata edition (vsk=2): the channel it addresses, its content, its signer (for the
MANAGE_METADATAauthority gate), and its version head (to advanceset_edition_head). - Entity
Head - 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. - Folded
Roster - The outcome of folding the control plane.
- Invite
Link Set - A folded per-creator InviteLinks edition (vsk=8): the creator who signed it, their active link
locators, and the version head. Applied only if
creatorheldCREATE_INVITE. - Root
Candidate - 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 holdsMANAGE_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_rosterresult 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 heldMANAGE_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 heldMANAGE_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 emptyrole_idsis 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 fixedversion == 1with noprev_hashand is exempt fromcheck_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 (seebuild_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 holdingCREATE_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 toauthor, 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 nextversion+ the prior edition’sprev_hash(the held head’sself_hash, orNonewithversion == 1for 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 syncbuild_role_editionis 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 thechannel-pseudonymderivation; 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=10atdissolved_locator), return its inner real-npub signer. The CALLER decides validity by checking that signer equals the proven owner (fail-closed authority).Nonefor 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 thenfold_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
#zwithout 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 bydissolved_pseudonym(NOTcontrol_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 currentcontrol_pseudonym(a current-epoch fast path); this is the cross-epoch path that survives a concurrent re-founding.