Expand description
Concord v2 service — the stateful orchestration binding the pure v2 modules
to storage + transport. Free functions, SessionGuard-gated at every write
(a swap_session can land at any await — see CLAUDE.md), mirroring the v1
service’s discipline.
First-cut scope (bots): create a community, send + fetch channel messages,
accept invites, publish a Guestbook Join. Rotation/refounding/moderation and
full roster folding layer on next. Signing is local-keys for now (bots hold
their nsec); a NIP-46 bunker create/send path is a documented follow-up (v2’s
genesis + chat seals are sign-only ops, so it composes — just needs the async
signer threaded through build_seal).
Structs§
- Authority
View - A community’s folded, delegation-authorized authority — the on-demand read
view (a paged control-plane fetch + fold, nothing persisted).
rolesis the owner-seeded authorized roster (shared algebra with v1);bannedthe enforced banlist.floored/head_entitieslet a writer detect a WITHHELD entity (floored locally but no head folded) before replacing it blind. - Fetched
Event - A chat event opened from a channel fetch, tagged with the epoch its key decrypted under.
- Minted
Link - A minted public link: the shareable URL plus the addressable bundle event to publish and the link keypair to retain (in the Invite List) for later refresh or revocation.
- Rekey
Follow - The outcome of a rekey-follow pass.
Functions§
- accept_
direct_ invite - Accept a Direct Invite: unwrap the 3313 giftwrap (Schnorr-verifying the seal), then run the shared accept path. The recipient’s consent IS this call. No network await precedes the accept, so the guard captured here suffices.
- accept_
parked_ invite - Accept a PARKED Direct Invite from its stored bundle JSON (the wrap was already
unwrapped + owner-verified at park time). Re-parses through the same fail-closed
bundle validation, then runs the shared accept path (which re-verifies the owner
root over the network).
inviter_hexis the parked seal signer, for Guestbook Join attribution. - accept_
public_ link - Accept a public invite link: fetch its bundle (revocation-aware) and join.
- bundle_
of - Build the §1 invite bundle for this community. Every channel is granted: a
Public channel carries the
community_rootas its “key” (the joiner derives the real secret from the root), a Private one its own key. The bundle self-certifies the owner, so the inviter’s identity is irrelevant to trust. - community_
is_ public - Whether this community is PUBLIC (CORD-05 §5): fold every creator’s Registry
(vsk-8) that its author is authorized for (
CREATE_INVITE, bound to their coordinate) into an aggregate live-link set — non-empty ⇒ a live link exists ⇒ Public; empty ⇒ Private. Retiring the last link is what flips it back. - create_
community - Create a fresh v2 community owned by the local identity: mint the genesis (self-certifying id + the two owner editions), persist, publish the genesis control editions, and announce the owner’s Guestbook Join. Returns the saved community.
- create_
private_ channel - Create a new PRIVATE channel (CORD-03 §2): mint a fresh id + an independent
random key at channel-epoch 1, deliver the key to every current member over the
rekey plane (CORD-06 §1), then announce the channel (vsk 2,
private). Epoch 0 is the root generation (“the first privatisation is epoch 1”), so the delivery commits its continuity to(0, community_root)— verifiable by every member and bound to THIS community’s root. The key ships BEFORE the announcement: an aborted attempt leaves only an unannounced crate (invisible), and a retry mints a fresh id, so there is no same-coordinate double-mint to fork on. Live public links are refreshed so a joiner’s bundle carries the key; a member who joins through the stale-bundle window keys up at the channel’s next rotation. - create_
public_ channel - Create a new PUBLIC channel (CORD-03 §2): mint a fresh id, publish its metadata
edition (vsk 2), and add it to the held community. A Public channel derives its Chat
Plane from the
community_root(no per-channel key), so other members fold it in on their next control follow with nothing to distribute. Returns the new channel id. Reader-gated byMANAGE_CHANNELS. - delete_
channel - Tombstone a channel (CORD-03 §2,
deleted: true) + drop it locally. Reader-gated byMANAGE_CHANNELS; the coordinate stays folded as a grave so peers hide it. - dissolve_
community - Owner dissolution / “Delete Community” (CORD-02 §9): publish the terminal
tombstone at the dissolved plane (
community_id-derived, epoch-free, so every past or present member resolves the same grave and a Refounding can never strand it). The tombstone’s presence IS the state; only the owner’s seal counts. Irreversible — on success the local hold is sealed read-only. - edit_
channel_ metadata - Add or edit a channel’s metadata (vsk 2, CORD-03 §2).
channel_idis the coordinate. Gated on the reader side byMANAGE_CHANNELS. - edit_
community_ metadata - Edit the community metadata (vsk 0, CORD-02 §6). Gated on the reader side by
MANAGE_METADATA. - ensure_
admin_ role - The community’s @admin role id: the folded Server-scope ADMIN_ALL role when one
exists, else (with
create_if_missing) a DETERMINISTIC mint — the same id on every device, so concurrent grants converge as editions of ONE entity instead of forking two Admin roles. - fetch_
authority - Fetch + fold the community’s current authority (CORD-04), paging older like
follow_controlwhile the fold is gapped so a busy control plane can’t push the roster off the newest window. A fetch failure degrades fail-safe: owner-only authority plus the PERSISTED banlist — nobody gains standing from an outage, and a ban never lifts on withheld data. - fetch_
channel - Fetch a channel’s newest messages — one page of
fetch_channel_history.limitis one relay-side bound across the whole epoch-author OR-set, not per epoch; deeper history pages backwards via the walk. - fetch_
channel_ history - Walk a channel’s history newest-first (CORD-03 §3 “clients load a Channel
newest-first and paginate backwards”), querying every held epoch’s Chat-Plane
address one
page-sized query at a time untilmax_pages, a drained relay, orkeep_pagingreturns false for a page (the caller’s “I already hold these” early stop — consulted only on pages that opened something, so junk at the address can’t fake exhaustion). Pages step by INCLUSIVEuntilwith wrap-id dedup, so a page boundary landing mid-second can’t skip siblings; a full page of only-already-seen wraps is a same-second WALL (relay filters are second-granular) and steps past it accepting that unseen same-second siblings beyond the relay cap are unreachable — logged, and a protocol-level limitation (themstag can’t be filtered server-side). - fetch_
public_ bundle - Fetch + decrypt the newest Live bundle at a public link’s coordinate
(
(33301, link_signer, "")). Revocation is authoritative-if-present: if ANY signer-valid tombstone is among the fetched events, refuse — never trust fetch ordering (a cross-relay union has no global newest-first sort, so a stale Live could otherwise win a partial-propagation race). Otherwise pick the newest valid Live bycreated_at. Read-only. - follow_
control - Re-fold this community’s Control Plane and apply the current metadata + public channel set to the held community, persisting any change. Called when a control-plane wrap arrives in realtime (a rename, a new channel, an edited description) so a long-running bot tracks the community mid-session instead of freezing at its join-time view.
- follow_
rekeys - Follow rekeys for a held community: advance the base (root) epoch and each Private channel’s epoch as far as authorized rotations allow, adopting the fresh key we’re still a recipient of at each step and dropping a scope we’ve been removed from. Persists the result. Called when a rekey wrap arrives in realtime so a long-running bot keeps decrypting after a rotation instead of going silent.
- grant_
admin - Grant the @admin role (minting it deterministically when absent), MERGED into the member’s existing grant — a grant entity replaces whole (CORD-04 §2), so a blind push would erase their other roles. Owner-only: the position-1 Admin is manageable only by position 0 (an equal never outranks it), and refusing before any publish keeps an unauthorized edition of the DETERMINISTIC admin entity from advancing this device’s own floor onto a head readers reject.
- grant_
roles - Grant or revoke a member’s Roles (vsk 3, CORD-04 §2). Empty
role_idsis a revoke. Gated on the reader side byMANAGE_ROLES+ outrank of every role + the member. - ingest_
guestbook_ event - Fold ONE live guestbook event into the store (the realtime path — no fetch). Returns whether it was new.
- is_
dissolved - Whether a valid owner-signed dissolution tombstone exists for this community on its relays (CORD-02 §9). A join refuses a dead community, and a live follow seals on sight. Fail-OPEN on a fetch error (absence of proof is not death), but any owner-verified tombstone found is authoritative.
- kick_
member - Cooperative Kick (CORD-04 §6, Guestbook plane): name the target; every reader
honors it iff the signer holds KICK and strictly outranks them (the coalesce’s
can_kick), so publishing without authority is inert. A kicked member may rejoin with a fresh invite — cryptographic severance is the ban/refound path. - leave_
community - Leave a community: publish a Guestbook Leave and tear down the local hold.
- memberlist
- Fold the Complete Memberlist from the Guestbook plane. The proven owner is ALWAYS a member (derived from the self-certifying community_id — no network, so a lost/evicted genesis Join can’t drop them). Observed authors — anyone seen publishing on a channel — are folded in FORWARD-only per CORD-02 §5, so a member whose Join was lost still counts.
- mint_
public_ link - Mint a public invite link for this community: a fresh token + link keypair, the
bundle encrypted under the token key and published at
(33301, link_signer, ""), and thebase/invite/<naddr>#<fragment>URL.baseis the deep-link domain (e.g.https://vectorapp.io); the fragment carries the token + bootstrap relays and never reaches a server. - preview_
bundle - The fold half of
preview_public_link, over an already-fetched bundle. Split out so a caller that only needs the community’s IDENTITY can read it off the bundle (it is self-certifying) and skip the Control-Plane walk entirely — the walk is the join gate, andaccept_public_linkruns it again regardless. - preview_
public_ link - Read-only rich preview of a public link: the decrypted bundle plus the LATEST display metadata folded live from the Control Plane (a v2 bundle deliberately carries no icon — the fold is the authority). Owner-root verification rides the fold, so a forged-root link can’t render a convincing preview; on a fold/transport failure the bundle snapshot is the fallback. Nothing persists — the caller hasn’t joined.
- refound_
community - Owner/admin Refounding (CORD-06 §3): roll the
community_rootto cryptographically removeremovedfrom a Private community (a Ban’s read-cut). Compacts the Control Plane under the new root (re-wraps each head VERBATIM — the inner owner/actor signatures survive, so no re-authoring), rekeys the base plus every Private channel (each sealed under the PRIOR root, D2, so a base-fork loser can still open them), and seeds the new epoch’s Guestbook snapshot. Requires BAN. - refresh_
public_ links - Refresh every live public link’s bundle behind its stable URL (CORD-05 §2) — e.g. after a Rekey/Refounding rolled the keys — by re-posting the bundle at the same coordinate with the CURRENT community state, so a link shared once keeps working across rotations. Best-effort.
- republish_
community_ list - Rebuild this account’s 13302 from its held v2 communities, MERGE with the remote
copy (preserving tombstones, other-device entries, unknown fields), and publish.
just_joinedis the community THIS call is recording a create/join for — the ONLY community whose entry is (re)stampednow, so it beats any prior tombstone (a deliberate re-join resurrects). Every OTHER held community that the remote has tombstoned is left tombstoned (a sibling device’s leave is NOT undone just because we joined something else — the W1 resurrection hole). Idempotent; best-effort — a list-publish failure never fails the membership change itself. - revoke_
admin - Strip the @admin role from the member’s grant, preserving their other roles.
A no-op when they don’t hold it. Owner-only, like
grant_admin. - revoke_
public_ link - Revoke a public link by its token hex (CORD-05 §2/§5): re-post its coordinate as a revocation tombstone (retiring the bundle behind the URL, so a fetcher finds the grave), tombstone the Invite List entry, and refresh the Registry. Retiring the LAST live link empties the Registry → the community reads Private (a Refounding is the owner’s separate read-cut).
- send_
chat_ message - Full chat send: threaded reply (NIP-C7
q, the parent’s(rumor_id, author)hex pair), NIP-30 custom-emoji pairs, and verbatim extra tags (NIP-92imetaattachments). Returns the message’s rumor id (hex). - send_
chat_ message_ at send_chat_messagewith an explicit event time. The rumor id is a pure function of its inputs, so a GUI that picksat_mscan precompute the id for its optimistic pending row — the in-process echo and the finalize then key the SAME id (the exact v1 pending → sent contract).- send_
delete - Cooperative in-plane delete (kind 5, NIP-09 semantics): peers stop rendering
target_id_hex. The wrap ciphertext on relays is scrubbed separately via the retained per-message stream key (seepublish_chat). - send_
direct_ invite - Gift-wrap a Direct Invite (kind 3313) of this community straight to
recipientand publish it to the community relays.expires_at_ms(unix ms) optionally bounds its shelf life;labelis echoed in the joiner’s Guestbook Join. The bundle hands over the keys; the recipient consents by accepting (nothing joins on receipt). Returns the wrap. - send_
edit - Edit one of your own messages (kind 3302): peers re-render
target_id_hexwith the replacement text. Author-enforced on the read side — only the original author’s edit folds. - send_
message - Send a text message to a channel. Derives the channel’s Chat-Plane group key (community_root for a Public channel, the channel key for a Private one), seals it encrypted, and publishes. Returns the message’s rumor id (hex).
- send_
reaction - React to a channel message (kind 7, NIP-25 shape).
target_id_hex/target_author_hexname the reacted-to message;target_kindis its rumor kind (kind::MESSAGE, orkind::COMMENTfor a threaded reply);emojicarries the NIP-30 pair whenemoji_contentis a custom:shortcode:. - send_
typing - Ephemeral typing indicator (kind 23311 in a 21059 wrap — relays never store it).
- send_
webxdc_ signal - WebXDC realtime peer signal (kind 3310) — the v2 twin of v1’s
publish_webxdc_signal: the same shared content shape, sealed on the channel’s chat plane, DURABLE (a reopening peer backfills a recent ad). Signed by the member’s real identity — a member can’t forge another player’s presence. Failure is non-fatal to callers (the next re-advertise covers a missed ad). - set_
banlist - Replace the Banlist (vsk 4, CORD-04 §4) with
banned(lowercase-hex npubs), the whole list on every edit. Gated on the reader side byBAN. - set_
role - Create or edit a Role (vsk 1, CORD-04 §2).
role.role_idis the coordinate; a rename or permission change is a versioned edit of the same id. Gated on the reader side byMANAGE_ROLES+ outrank. - stored_
memberlist - The memberlist from LOCAL state only: the persisted Guestbook, plus locally
observed authors (the synced events DB), plus roster grantees, minus the
banlist. Instant and offline-correct;
sync_guestbook(post-join, boot, reconnect, live ingest) keeps the store current. The livememberlistremains the authoritative walk — a refounding’s rekey recipient set must never trust a possibly-stale store. - sync_
community_ list - Sync memberships from the 13302 across devices: fetch this account’s list from
bootstrap_relays(its held communities’ relays plus any caller-supplied set for a fresh device), and JOIN every live entry not already held — reconstructing the community from its join material and re-verifying the owner root. Returns the newly-rehydrated communities (so the caller can subscribe + notify). - sync_
guestbook - Catch the persisted Guestbook up from its stored cursor (a fresh hold seeds from zero). The fetch straddles the network, so the session re-checks before the store writes. Returns the events that were NEW to the store — the caller surfaces them (presence lines) and refreshes on non-empty.