This commit is contained in:
2026-09-22 20:34:01 +07:00
parent 706cc72c1f
commit 08c1687d87
6 changed files with 433 additions and 36 deletions
+15 -1
View File
@@ -312,13 +312,19 @@ as an inline row only when its author passes
## Membership
```rust
let states = cord02::guestbook::coalesce(&rumors, now_ms, Some(&refounder_pk), |actor, target, citation| {
let states = cord02::guestbook::coalesce(&rumors, now_ms, &refounders, |actor, target, citation| {
citation_ok(&owner, &community_id, actor, citation, &control.roles.floors)
&& control.roles.can_act_on_member(actor, &owner, target, Permissions::KICK)
});
let members = cord02::guestbook::complete_memberlist(&states, &observed, &granted, &control.banned, &BTreeMap::new());
```
- `refounders` is the set of npubs whose rotations minted an epoch this client
verified (`CommunityState.refounders`) — a snapshot chunk is honored only from
one of them, and an empty set honors none. A rotation that delivered this
client no key still names its minter, so the set is filled by any base rotation
whose continuity verifies against a root held (`rekey::walk`); a List entry's
`refounder`, read by `list::JoinMaterial::refounder`, is the other source.
- `observed` is npub → ms for every author this client has seen publish anything
usable, which is what makes a member visible before their Join arrives. Only
count it forward.
@@ -553,6 +559,14 @@ fragment at every index below it. `merge` resolves a `frags` disagreement to the
larger value. (`13302`, the single-event List, is retired by the spec — a
replaceable kind cannot fragment.)
A join material may also carry two hex extensions this crate does not write but
does read, because they are the only place a device that never held a rotation can
learn who minted an epoch: `refounder` names the npub whose Refounding minted the
entry's `root_epoch`, and `held_roots` is the retained prior epochs
(`[{epoch, key, refounder?, control_pk?, retired_at?}]`). `JoinMaterial::refounder()`
and `JoinMaterial::held_roots()` decode both; unknown fields are round-tripped
regardless, so a document that carries them keeps them.
The payload's 32-byte values are **unpadded base64url at every depth**, which is
section-scoped to §8: CORD-05 invites stay hex. The writer re-encodes them on
every serialization, so its output is always the canonical 43-character spelling;