Event examples
Non-normative. One example per event kind in the frozen registry (CORD-02, Appendix B). The CORD documents are the source of truth; if an example here disagrees with a CORD, the CORD wins.
Conventions used throughout:
<angle brackets>are placeholders. Keys, ids, and hashes are lowercase hex (x-only pubkeys and ids are 32 bytes / 64 hex chars) unless noted.nip44_encrypt(key, {...})stands for the NIP-44 ciphertext of the serialized inner event — shown structurally so the nesting is readable.- Rumors (the innermost events) are unsigned and carry no
sig; the seal carries the author’s real signature (CORD-01). ["ms", "<0..999>"]is the sub-second remainder; true event time iscreated_at * 1000 + ms(CORD-02 §4).- Examples are illustrative, not verifiable test vectors: ids, signatures, and derived addresses are placeholders, not real derivations.
Contents
Section titled “Contents”| Kind | Function | Example |
|---|---|---|
| 1059 | Gift wrap (durable envelope) | §1.1 |
| 20013 | Encrypted seal | §1.1 |
| 20014 | Plaintext seal (Control Plane) | §1.2 |
| 21059 | Ephemeral gift wrap | §1.3 |
| 9 | Message | §2.1 |
| 1111 | Threaded reply | §2.2 |
| 7 | Reaction | §2.3 |
| 5 | Delete | §2.4 |
| 3302 | Edit | §2.5 |
| 3310 | WebXDC peer signal | §2.6 |
| 23311 | Typing indicator (ephemeral) | §2.7 |
| 23313 | Voice presence (ephemeral) | §2.8 |
| 1740 | Timer notice | §2.9 |
| 3306 | Join / Leave | §3.1 |
| 3309 | Kick | §3.2 |
| 3312 | Guestbook snapshot | §3.3 |
| 3308 | Control edition (per vsk) |
§4 |
| 3303 | Rekey blobs | §5 |
| 33301 | Public invite bundle | §6.1 |
| 13302 | Community List | §6.2 |
| 13303 | Invite List | §6.3 |
| 3313 | Direct invite (standard NIP-59 wrap) | §7 |
Retired kinds (3300, 3301, 3304, 3305, 3307, 3311, 23308) have no examples: their numbers are burned, never reused (CORD-02, Appendix B).
1. The envelope
Section titled “1. The envelope”Every durable plane event is the same three-layer shape (CORD-01): a kind 1059 wrap signed by the plane’s derived stream key, containing a seal signed by the author’s real key, containing the unsigned rumor that carries the functional kind. Sections 2–5 show only rumors; each rides inside this envelope at its plane’s address.
1.1 Kind 1059 — the wrap, encrypted seal
Section titled “1.1 Kind 1059 — the wrap, encrypted seal”Chat, Guestbook, and rekey planes use the encrypted seal (kind 20013): the rumor is NIP-44-encrypted again inside the already-encrypted wrap, so it can never be lifted out as a standalone public event (CORD-02 §5).
Because the wrap reverses NIP-59 (fixed author, ephemeral p), delivery depends on relays not enforcing NIP-59’s optional p-tag guard (“relays SHOULD only serve kind 1059 events intended for the marked recipient”): members subscribe by the stream author, and nobody is the random p. Communities must choose relays accordingly.
{ "id": "<wrap id>", "kind": 1059, "pubkey": "<plane's stream pubkey>", // e.g. channel_pk, guestbook_pk (CORD-02 §4) "content": nip44_encrypt(conv_key, { // conv_key = NIP-44 self-ECDH of the stream key "id": "<seal id>", "kind": 20013, // encrypted seal "pubkey": "<author's real pubkey>", "content": nip44_encrypt(conv_key, { "id": "<rumor id>", "kind": 9, // the functional kind (any §2–§5 rumor) "pubkey": "<author's real pubkey>", "content": "Hey chat!", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "417"] ], "created_at": 1686840217 }), "tags": [], "created_at": 1686840217, "sig": "<author's real signature>" }), "tags": [ ["p", "<random ephemeral pubkey>"] // ephemeral "p", fixed author — NIP-59 reversed (CORD-01). // Keep this key's secret if you want to NIP-09-delete the wrap later (CORD-01). ], "created_at": 1686840217, "sig": "<stream key signature>"}1.2 Kind 20014 — the plaintext seal
Section titled “1.2 Kind 20014 — the plaintext seal”The Control Plane only (CORD-02 §5). Same wrap, but the seal’s content holds the rumor’s serialized JSON string rather than ciphertext (byte-verbatim, CORD-01), so a compaction can re-wrap the signed edition into a new epoch with the signature intact (CORD-06). See §4 for the full Control edition example inside this seal.
{ "kind": 1059, "pubkey": "<control_pk>", // the control_root-derived signer: only staff can mint this wrap (CORD-02 §5) "content": nip44_encrypt(control_conv_key, { // the community_root-derived read key: every member decrypts (CORD-02 §5) "id": "<seal id>", "kind": 20014, // plaintext seal "pubkey": "<actor's real pubkey>", "content": json_stringify({ /* the kind 3308 rumor, §4 */ }), // the rumor's serialized JSON string, not a ciphertext "tags": [], "created_at": 1686840217, "sig": "<actor's real signature>" }), "tags": [ ["p", "<random ephemeral pubkey>"] ], "created_at": 1686840217, "sig": "<control signer signature>"}1.3 Kind 21059 — the ephemeral wrap
Section titled “1.3 Kind 21059 — the ephemeral wrap”Identical structure to 1059, but relays MUST NOT store it: broadcast to live subscribers and gone. Used only for the typing indicator (§2.7).
{ "kind": 21059, "pubkey": "<channel_pk>", "content": nip44_encrypt(conv_key, { /* kind 20013 seal around a kind 23311 or 23313 rumor, §2.7–2.8 */ }), "tags": [ ["p", "<random ephemeral pubkey>"] ], "created_at": 1686840217, "sig": "<channel stream signature>"}2. Chat Plane rumors
Section titled “2. Chat Plane rumors”All Chat rumors ride an encrypted seal (§1.1) at the Channel’s address, and MUST commit ["channel", <channel_id>] and ["epoch", <n>] inside the author-signed rumor; a receiver checks both against the key that opened the wrap and drops a mismatch (CORD-03 §3).
When the Community’s disappearing-messages timer is set (CORD-08), every durable Chat rumor and its outer wrap also carry NIP-40 ["expiration", "<created_at + timer>"] — except deletes (§2.4) and timer notices (§2.9), which never expire. The examples below omit the tag.
2.1 Kind 9 — Message
Section titled “2.1 Kind 9 — Message”NIP-C7 shape: the content is the message text. (Not NIP-29, whose messages require an h tag and whose kind 9 definition has since been dropped; the registry assigns kind 9 to NIP-C7.)
{ "id": "<rumor id>", "kind": 9, "pubkey": "<author>", "content": "Hey chat!", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "417"] ], "created_at": 1686840217}An inline quote embeds another message in the timeline with a q tag (NIP-C7), citing the quoted message’s rumor id (never the outer wrap’s, which differs per re-wrap). It stays a kind 9 and renders as an ordinary timeline row that happens to carry a quoted card:
{ "kind": 9, "pubkey": "<author>", "content": "Welcome!", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "902"], ["q", "<quoted message rumor id>", "", "<quoted author>"] ], "created_at": 1686840304}2.2 Kind 1111 — Threaded reply
Section titled “2.2 Kind 1111 — Threaded reply”A threaded reply is a distinct action from an inline quote: it lives in a thread hanging off its root, not as a top-level timeline row. It is a NIP-22 comment (kind 1111), not a kind 9 — reusing q for this would conflate “quote this inline” with “reply in a thread”. Uppercase K/E/P pin the immutable thread root; lowercase k/e/p pin the immediate parent. All ids are rumor ids (never the outer wrap’s). When the parent is itself a reply, its uppercase root tags are inherited verbatim, so the root stays stable at any nesting depth:
{ "kind": 1111, "pubkey": "<author>", "content": "Replying in the thread!", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "744"], ["K", "9"], ["E", "<thread root rumor id>", "", "<root author>"], ["P", "<root author>"], ["k", "9"], ["e", "<immediate parent rumor id>", "", "<parent author>"], ["p", "<parent author>"] ], "created_at": 1686840360}Reactions, edits, and deletes target a threaded reply exactly as they target a kind-9 message (by its rumor id); the k tag they carry names the target’s kind (1111 for a reply, 9 for a message).
2.3 Kind 7 — Reaction
Section titled “2.3 Kind 7 — Reaction”NIP-25 shape: content is the reaction ("+", an emoji, …), tags name the reacted-to message, its author, and its kind.
{ "kind": 7, "pubkey": "<reactor>", "content": "🔥", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "112"], ["e", "<message rumor id>"], ["p", "<message author>"], ["k", "9"] ], "created_at": 1686840350}2.4 Kind 5 — Delete
Section titled “2.4 Kind 5 — Delete”NIP-09 shape: e tags name the author’s own rumors to delete, k their kind, content an optional reason. A member’s delete of their own past message is honored always — even after Dissolution (CORD-02 §9).
Note this delete names a rumor id relays never saw, so it is semantic within the plane only — members stop rendering the message, but the wrap ciphertext stays on relays. Scrubbing the ciphertext itself is a separate, best-effort NIP-09 delete of the wrap by its p tag (CORD-01), possible only if the publishing client kept the ephemeral key.
{ "kind": 5, "pubkey": "<author>", "content": "", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "533"], ["e", "<own message rumor id>"], ["k", "9"] ], "created_at": 1686841000}2.5 Kind 3302 — Edit
Section titled “2.5 Kind 3302 — Edit”The CORDs register the kind but don’t yet pin its fields; this shape is illustrative. The e tag names the author’s own message being edited, content the replacement text.
{ "kind": 3302, "pubkey": "<author>", "content": "Hey chat! (fixed the typo)", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "781"], ["e", "<own message rumor id>"] ], "created_at": 1686840610}2.6 Kind 3310 — WebXDC peer signal
Section titled “2.6 Kind 3310 — WebXDC peer signal”Realtime peer signaling for WebXDC apps. The CORDs register the kind but don’t yet pin its payload; content is the app-level payload, opaque to the protocol.
{ "kind": 3310, "pubkey": "<author>", "content": "<app payload>", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "266"] ], "created_at": 1686840700}2.7 Kind 23311 — Typing indicator
Section titled “2.7 Kind 23311 — Typing indicator”An ephemeral action: same seal-and-rumor shape at the same Channel address, but the outer wrap is kind 21059 (§1.3) and the rumor kind is ephemeral-range too, so relays never store any layer of it. Presence of the event is the signal; the rumor carries nothing.
{ "kind": 23311, "pubkey": "<author>", "content": "", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "45"] ], "created_at": 1686840216}2.8 Kind 23313 — Voice presence
Section titled “2.8 Kind 23313 — Voice presence”Ephemeral like the typing indicator (21059 wrap, §1.3): call heartbeats are realtime-only, nothing worth storing. The content is the verb; a joined repeats every 30 seconds carrying the broker-assigned SFU identity and the broker origin, goes stale after 90 seconds (three missed heartbeats), and a left omits both (CORD-07 §4).
// Joined (also the heartbeat){ "kind": 23313, "pubkey": "<member>", "content": "joined", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["identity", "<SFU identity>"], ["broker", "https://broker.example"], ["ms", "417"] ], "created_at": 1686840217}
// Left (best-effort; a missed one heals by staleness){ "kind": 23313, "pubkey": "<member>", "content": "left", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "902"] ], "created_at": 1686840305}2.9 Kind 1740 — Timer notice
Section titled “2.9 Kind 1740 — Timer notice”Posted by staff after changing the Community’s disappearing-messages timer (CORD-08 §4), one per Channel whose key they hold — the same kind and timer tag NIP-17 disappearing-DM clients use. Rendered as an inline notice row; displayed only if the author holds MANAGE_METADATA in the fold. Never carries an expiration tag itself.
{ "kind": 1740, "pubkey": "<staff member>", "content": "", "tags": [ ["channel", "<channel_id>"], ["epoch", "0"], ["ms", "233"], ["timer", "2592000"] // the new timer in seconds; "0" = turned off ], "created_at": 1686845000}3. Guestbook Plane rumors
Section titled “3. Guestbook Plane rumors”Encrypted seals (§1.1) at guestbook_pk (CORD-02 §5). The Guestbook coalesces flat — one final state per npub, latest wins by millisecond time, ties broken by the lower rumor id.
3.1 Kind 3306 — Join / Leave
Section titled “3.1 Kind 3306 — Join / Leave”Self-signed; the content is the verb. A Join MAY carry invite attribution echoed from the bundle (CORD-05 §1).
// Join, with optional invite attribution{ "kind": 3306, "pubkey": "<member>", "content": "join", "tags": [ ["ms", "128"], ["invite", "<creator pubkey>", "Reddit"] // optional, Joins only ], "created_at": 1719800000}
// Leave{ "kind": 3306, "pubkey": "<member>", "content": "leave", "tags": [ ["ms", "660"] ], "created_at": 1722400000}3.2 Kind 3309 — Kick
Section titled “3.2 Kind 3309 — Kick”Admin-signed, names its target, and cites the Grant it acts under (the vac, CORD-04 §5). Honored only if the signer holds KICK and strictly outranks the target.
{ "kind": 3309, "pubkey": "<admin>", "content": "", "tags": [ ["ms", "301"], ["p", "<target pubkey>"], ["vac", "<grant eid>", "<grant version>", "<grant edition hash>"] ], "created_at": 1722410000}3.3 Kind 3312 — Guestbook snapshot
Section titled “3.3 Kind 3312 — Guestbook snapshot”Refounder-signed, seeding the new epoch’s Guestbook after a Refounding (CORD-02 §5). Present members only, chunked at 400 per event; all n chunks share one snapshot id and one timestamp.
{ "kind": 3312, "pubkey": "<refounder>", "content": "[\"<member pubkey>\", \"<member pubkey>\", \"<member pubkey>\"]", "tags": [ ["ms", "0"], ["snap", "<snapshot id>", "1", "2"] // chunk 1 of 2 ], "created_at": 1722500000}4. Control Plane — kind 3308 editions
Section titled “4. Control Plane — kind 3308 editions”Every authority action is a kind 3308 edition rumor inside a plaintext seal (§1.2) at control_pk — the staff-held signer’s address (CORD-02 §5): every member subscribes, verifies, and reads it, but only a control_root holder can publish to it. The tags carry the edition machinery (CORD-04 §1); the content is the entity’s new state as JSON, its structure selected by vsk.
The common frame:
{ "kind": 3308, "pubkey": "<actor's real pubkey>", "content": "<the entity's new state, per-vsk below, as a JSON string>", "tags": [ ["vsk", "1"], // entity type (registry below) ["eid", "<entity id, 32-byte hex>"], // the stable coordinate ["ev", "4"], // this edition's version, climbs from 1 ["ep", "<prev edition hash>"], // chain link, absent on the first edition ["vac", "<grant eid>", "<grant version>", "<grant edition hash>"] // absent when the owner acts ], "created_at": 1686840217}Per-vsk content payloads:
vsk 0 — Community metadata (CORD-02 §6)
Section titled “vsk 0 — Community metadata (CORD-02 §6)”eid = the community_id. Gated by MANAGE_METADATA.
{ "name": "Vector", "description": "Private messaging, no compromises.", "relays": ["wss://jskitty.com/nostr", "wss://asia.vectorapp.io/nostr"], "icon": { "url": "https://blossom.example/…", "key": "<hex>", "nonce": "<hex>", "hash": "<sha256 hex>" }, "banner": { "url": "https://blossom.example/…", "key": "<hex>", "nonce": "<hex>", "hash": "<sha256 hex>" }, "message_expiration": 2592000, "custom": { "rules": "Be excellent to each other." }}vsk 1 — Role (CORD-04 §2)
Section titled “vsk 1 — Role (CORD-04 §2)”eid = the role_id, random 32 bytes minted at creation. Gated by MANAGE_ROLES.
{ "role_id": "<role_id hex>", "name": "Moderator", "position": 2, "permissions": "40", // decimal string: 1<<3 KICK | 1<<5 MANAGE_MESSAGES "scope": { "kind": "server" }, // or {"kind":"channel","channel_id":"<hex>"} "color": 15158332}vsk 2 — Channel metadata (CORD-03 §2)
Section titled “vsk 2 — Channel metadata (CORD-03 §2)”eid = the channel_id. Gated by MANAGE_CHANNELS. Every Channel is callable (CORD-07); there is no per-Channel voice flag.
{ "name": "general", "private": false }{ "name": "lounge", "private": false }A deletion is an edition setting the terminal flag:
{ "name": "general", "private": false, "deleted": true }vsk 3 — Grant (CORD-04 §2)
Section titled “vsk 3 — Grant (CORD-04 §2)”eid = grant_locator(community_id, member) (CORD-02 A.6). Honored only if the signer outranks every Role handed out; empty role_ids is a revoke. A staff-making Grant also delivers the Control Plane write secret in control_wrap (CORD-04 §3): NIP-44 ciphertext under the granter↔member pairwise key, its plaintext the fixed-width 40 bytes epoch_be[8] ‖ control_root[32], adopted only if the secret derives to the recipient’s held control_pk at that epoch. Every other reader treats it as opaque bytes.
{ "member": "<member pubkey>", "role_ids": ["<role_id hex>", "<role_id hex>"], "control_wrap": "<base64>" // optional: staff write key delivery (CORD-04 §3)}vsk 4 — Banlist (CORD-04 §4)
Section titled “vsk 4 — Banlist (CORD-04 §4)”eid = banlist_locator(community_id) (CORD-02 A.6). The whole list, replaced entire on every edit; signer must hold BAN.
["<banned pubkey>", "<banned pubkey>"]vsk 8 — Invite-link registry (CORD-05 §5)
Section titled “vsk 8 — Invite-link registry (CORD-05 §5)”eid = invite_links_locator(community_id, creator) (CORD-02 A.6). Locators only, never tokens or URLs; honored while its author holds CREATE_INVITE.
["<link_signer pubkey hex>", "<link_signer pubkey hex>"]vsk 11 — Pin List (CORD-04 §7)
Section titled “vsk 11 — Pin List (CORD-04 §7)”eid = pins_locator(community_id, channel_id) (CORD-02 A.6). One list per Channel, replaced entire on every edit; signer must hold PIN_MESSAGES. Each entry carries the pinned message’s original seal verbatim plus its one-shot NIP-44 message keys, so any reader able to open the list’s form — including one holding no historical keys — verifies author, words, and time from the pin alone.
// Public Channel: plaintext — the plane's wrap is the gate, and compaction// re-wrapping it to each new root keeps it readable forever.{ "entries": [ { "seal": { /* the original kind-20013 seal event, byte-verbatim */ }, "keys": "<76 bytes hex: chacha_key[32] || chacha_nonce[12] || hmac_key[32]>", "wrap": "<hex event id>", // optional: jump-to-context locator "edit": { // optional: the author's own Edit (kind 3302), "seal": { /* … */ }, // proven the same way, so a keyless reader "keys": "<76 bytes hex>" // never reads superseded words as current } }] }// Private Channel: the same list, NIP-44-sealed under the Channel's group key// at the named epoch (self-ECDH conversation key, CORD-01).{ "epoch": "4", "sealed": nip44_encrypt(channel_conv_key, { "entries": [ … ] }) }Caps: 25 entries and 32,768 bytes of content, judged on the carried bytes — a violating edition still folds and chains, but reads as an empty list (CORD-04 §7). An entry failing verification (seal kind, signature, MAC, decryption, unpad/parse, rumor kind, author equality, or the rumor’s channel tag not matching this list’s Channel) is dropped alone; an edit bundle failing the same checks — with kind 3302, and additionally the Edit’s author matching the original’s and its e tag naming the original — costs only the revision, never the pin. A member’s deletion of their own message kills its pin.
vsk 10 — Dissolved tombstone (CORD-02 §9)
Section titled “vsk 10 — Dissolved tombstone (CORD-02 §9)”Owner-signed, chainless, exempt from version discipline — published at dissolved_pk, not the Control Plane address. Presence of one valid owner-signed edition is the state.
The eid is the community_id, and a verifier MUST check it: dissolved_pk derives from that id with no secret, so the plane is public to read and to sign at, and a payload naming no Community would let an owner’s tombstone for one be re-wrapped to kill another (CORD-02 §9).
{ "kind": 3308, "pubkey": "<owner>", "content": "", "tags": [ ["vsk", "10"], ["eid", "<community_id>"] // binds the tombstone to the Community it kills // chainless: no ev, no ep, no vac ], "created_at": 1725000000}Remaining vsk values carry no edition: 5 is reserved, 6/9 are claimed by the addressable invite marker (§6.1), 7 is retired.
5. Rekeys — kind 3303
Section titled “5. Rekeys — kind 3303”Delivered at a rekey address derived from the prior secret (CORD-06 §2), wrapped and encrypted-sealed like any stream event. The seal’s npub is the Rotator, whose authority a receiver verifies before accepting anything.
{ "kind": 3303, "pubkey": "<rotator's real pubkey>", "content": "[ {\"locator\": \"<hex>\", \"wrapped\": \"<base64>\"}, {\"locator\": \"<hex>\", \"wrapped\": \"<base64>\"} ]", "tags": [ ["scope", "<channel_id hex>"], // all-zero hex = the community_root (a Refounding) ["newepoch", "3"], ["prevepoch", "2"], ["prevcommit", "<epoch-key commitment hex, CORD-02 A.5>"], ["chunk", "1", "2"] // this event is chunk 1 of 2 for the rotation ], "created_at": 1722500000}Each wrapped plaintext is fixed-width per form (CORD-06 §1): a Channel rotation’s is 72 bytes — scope_id[32] ‖ epoch_be[8] ‖ new_key[32] — while a base rotation’s appends the next epoch’s Control Plane keys, 104 bytes for a member (… ‖ new_control_pk[32]) and 136 for a staff recipient (… ‖ new_control_root[32]). All are NIP-44-encrypted under the Rotator↔recipient pairwise key; the recipient finds their blob by computing their locator (CORD-06 §2) and verifies the inner scope and epoch against the tags before accepting the key — a staff recipient also that the delivered secret derives to the delivered pubkey. A 72-byte base blob marks a legacy, pre-split rotation (CORD-06 §3): honored when reading old epochs — its acceptor folds that epoch’s Control at the legacy address — never minted by a compliant Rotator.
6. Outside the wrap
Section titled “6. Outside the wrap”Three kinds relays see bare, signed by ordinary (or per-link) keys.
6.1 Kind 33301 — public invite bundle (CORD-05 §2)
Section titled “6.1 Kind 33301 — public invite bundle (CORD-05 §2)”Addressable, authored by the link’s dedicated link_signer keypair with an empty d identifier — the coordinate (33301, link_signer, "") is exactly what the link’s naddr names. The vsk tag marks it live (6) or a revocation tombstone (9).
The guard is the coordinate itself: a squatter’s event is a different author, hence a different coordinate, and never matches the fetcher’s filter. Since the link_signer secret lives only in the creator’s Invite List (CORD-05 §4), a link-holder can preview and join but can never re-post, replace, or tombstone the bundle. And a hostile bundle can’t smuggle a false owner regardless — the community_id self-certifies (CORD-05 §1).
// live bundle{ "kind": 33301, "pubkey": "<link_signer pubkey>", "content": "<nip44_encrypt(bundle_key, bundle)>", "tags": [ ["d", ""], ["vsk", "6"] ], "created_at": 1719800000, "sig": "<link_signer signature>"}The encrypted bundle’s plaintext (CORD-05 §1):
{ "community_id": "<hex>", "owner": "<owner pubkey>", "owner_salt": "<hex>", // verify: community_id == sha256("concord/community" || owner || salt) "community_root": "<hex>", "root_epoch": 0, "control_pk": "<hex>", // the Control Plane signer's pubkey at that epoch (CORD-02 §5) "channels": [ { "id": "<channel_id>", "key": "<hex>", "epoch": 1, "name": "testers" } ], "relays": ["wss://jskitty.com/nostr", "wss://relay.ditto.pub"], "name": "Vector", "icon": { "url": "https://blossom.example/…", "key": "<hex>", "nonce": "<hex>", "hash": "<sha256 hex>" }, "expires_at": 1735689600000, // optional, unix ms "creator_npub": "<creator pubkey>", // optional attribution "label": "Reddit" // optional attribution}Revoking the link re-posts the same coordinate as a tombstone:
{ "kind": 33301, "pubkey": "<link_signer pubkey>", "content": "", "tags": [ ["d", ""], ["vsk", "9"] ], "created_at": 1722400000, "sig": "<link_signer signature>"}6.2 Kind 13302 — Community List (CORD-02 §8)
Section titled “6.2 Kind 13302 — Community List (CORD-02 §8)”Replaceable, one per user, signed by their real key, NIP-44-encrypted to self. A client convenience, never Community state.
{ "kind": 13302, "pubkey": "<member's real pubkey>", "content": "<nip44_encrypt(self, list)>", "tags": [], "created_at": 1722400000, "sig": "<member's real signature>"}The encrypted list’s plaintext:
{ "entries": [ { "community_id": "<hex>", "seed": { /* join material at the earliest epoch held — only ever moves backward on merge */ }, "current": { /* join material at the freshest epoch — replaced on every Refounding or rename */ }, "added_at": 1719800000000 // ms } ], "tombstones": [ { "community_id": "<hex>", "removed_at": 1722400000000 } ]}Join material is the bundle’s membership subset: community_id, owner, owner_salt, community_root, root_epoch, control_pk, channels, relays, name, plus control_root when held (CORD-02 §2) — never the icon, never the link fields.
6.3 Kind 13303 — Invite List (CORD-05 §4)
Section titled “6.3 Kind 13303 — Invite List (CORD-05 §4)”Replaceable, one per user, signed by their real key, NIP-44-encrypted to self: the creator’s private bookkeeping for minted links.
{ "kind": 13303, "pubkey": "<creator's real pubkey>", "content": "<nip44_encrypt(self, list)>", "tags": [], "created_at": 1722400000, "sig": "<creator's real signature>"}The encrypted list’s plaintext:
{ "entries": [ { "token": "<hex>", // the link's unlock secret AND its merge key "signer_sk": "<hex>", // the link_signer secret (CORD-05 §2) "community_id": "<hex>", "url": "https://vectorapp.io/invite/<naddr>#<fragment>", "label": "Reddit", // optional "created_at": 1719800000, "expires_at": 1722400000 // optional } ], "tombstones": [ { "token": "<hex>", "community_id": "<hex>" } ]}7. Kind 3313 — Direct invite
Section titled “7. Kind 3313 — Direct invite”The one event riding a standard NIP-59 giftwrap (CORD-05 §6): ephemeral wrap author, the recipient in the p tag, a kind 13 seal — not the reversed stream wrap of §1 — plus the outer ["k", "3313"] that makes invites indexable. A recipient fetches exactly their invites with {"kinds":[1059], "#p":["<me>"], "#k":["3313"]}, no bulk decryption of their giftwrap inbox required. The outer tag is an unsigned hint; the rumor’s kind is the authority.
The rumor carries the §6.1 CommunityInvite bundle itself as its content — no coordinate, no token, nothing to fetch — validated exactly as a fetched bundle: the self-certifying community_id, the CORD-05 §1 bounds, expires_at. It is a key handoff, not a standing door: unrevocable once landed, absent from the Registry, and it never flips the Community Public (CORD-05 §6). Nothing happens on receipt — no relay connection, no icon fetch, no Join — until the user accepts.
{ "id": "<wrap id>", "kind": 1059, "pubkey": "<ephemeral pubkey, single-use>", "content": nip44_encrypt(ephemeral↔recipient, { "id": "<seal id>", "kind": 13, // standard NIP-59 seal "pubkey": "<inviter's real pubkey>", "content": nip44_encrypt(inviter↔recipient, { "id": "<rumor id>", "kind": 3313, "pubkey": "<inviter's real pubkey>", "content": json_stringify({ /* the CommunityInvite bundle, §6.1 */ }), "tags": [], "created_at": 1719800000 }), "tags": [], "created_at": 1719764213, // NIP-59: tweaked into the past "sig": "<inviter's real signature>" }), "tags": [ ["p", "<recipient pubkey>"], // classic NIP-59: fixed recipient, ephemeral author ["k", "3313"], // the index hint (CORD-05 §6) ["expiration", "1735689600"] // optional NIP-40, matching the bundle's expires_at ], "created_at": 1719731502, // NIP-59: tweaked into the past "sig": "<ephemeral key signature>"}