//! Buzz V2 kind number registry. //! //! This module is the authoritative source for Buzz kind numbers. //! All constants are `u32` — NIP-01 specifies kind as an unsigned integer, //! and u32 covers the full range without truncation. // Standard NIP kinds /// NIP-01: User profile metadata. pub const KIND_PROFILE: u32 = 0; /// NIP-01: Short text note. pub const KIND_TEXT_NOTE: u32 = 1; /// NIP-02: Contact list / follow list. pub const KIND_CONTACT_LIST: u32 = 3; /// NIP-51: Mute list (replaceable, 10000–19999 range) — pubkeys/events/threads/words a user has muted. /// /// User-owned global state, keyed by `(pubkey, kind)`. Same ownership/scope shape as kind:3. pub const KIND_MUTE_LIST: u32 = 10000; /// NIP-51: Pin list (replaceable) — events the user has pinned to their profile. /// /// User-owned global state, keyed by `(pubkey, kind)`. The events referenced may live in /// channels, but the pin list itself is profile-level state. pub const KIND_PIN_LIST: u32 = 10001; /// NIP-65: Relay list metadata (replaceable) — read/write relay preferences for the outbox model. /// /// User-owned global state, keyed by `(pubkey, kind)`. Tags are `["r", url]` or /// `["r", url, "read"]` / `["r", url, "write"]`. pub const KIND_NIP65_RELAY_LIST_METADATA: u32 = 10002; /// NIP-51: Bookmark list (replaceable) — events/articles/hashtags/URLs the user has bookmarked. /// /// User-owned global state, keyed by `(pubkey, kind)`. References content but is not itself /// channel-scoped content. pub const KIND_BOOKMARK_LIST: u32 = 10003; /// NIP-51: Emoji list (replaceable) — user preferred emojis and pointers to emoji sets. pub const KIND_EMOJI_LIST: u32 = 10030; /// NIP-51: Follow set (parameterized replaceable, 30000–39999 range) — named curated lists of pubkeys. /// /// User-owned, keyed by `(pubkey, kind, d_tag)`. Allows multiple named follow lists on top of /// the single kind:3 contact list (e.g. "close-friends", "news", "devs"). pub const KIND_FOLLOW_SET: u32 = 30000; /// NIP-51: Bookmark set (parameterized replaceable) — named curated bookmark collections. /// /// User-owned, keyed by `(pubkey, kind, d_tag)`. pub const KIND_BOOKMARK_SET: u32 = 30003; /// NIP-51 / NIP-30: Emoji set (parameterized replaceable). /// /// User-owned, keyed by `(pubkey, kind, d_tag)`. Each member publishes their own /// kind:30030 set (signed as themselves); the workspace emoji "palette" is the /// client-side union of everyone's sets — a view computed on read, not stored /// state. Ingest allowlists member-authored kind:30030/10030 (see /// `required_scope_for_kind`), and the generic NIP-33 replace path keeps only the /// latest per `(pubkey, d_tag)`. pub const KIND_EMOJI_SET: u32 = 30030; /// NIP-01: Channel metadata (replaceable). Not used by Buzz today. pub const KIND_CHANNEL_METADATA: u32 = 41; /// NIP-09: Event deletion request. pub const KIND_DELETION: u32 = 5; /// NIP-25: Content is emoji char or `+`/`-`. pub const KIND_REACTION: u32 = 7; /// NIP-17: Outer envelope for private DMs — hides sender, content, timestamp. pub const KIND_GIFT_WRAP: u32 = 1059; /// NIP-94: File metadata attachment. pub const KIND_FILE_METADATA: u32 = 1063; /// NIP-23: Long-form content (articles, blog posts, RFCs). /// Parameterized replaceable (NIP-33, 30000–39999 range) — keyed by `(pubkey, kind, d_tag)`. /// Stored globally (channel_id = NULL); author-owned, not channel-scoped. pub const KIND_LONG_FORM: u32 = 30023; /// NIP-38: User status (general, music, or custom d-tag). /// Parameterized replaceable (NIP-33, 30000–39999 range) — keyed by `(pubkey, kind, d_tag)`. /// Stored globally (channel_id = NULL); user-owned personal data, not channel-scoped. pub const KIND_USER_STATUS: u32 = 30315; /// NIP-78 / NIP-RS: Per-client read state blob for cross-device read position sync. /// Parameterized replaceable (NIP-33, 30000–39999 range) — keyed by `(pubkey, kind, d_tag)`. /// Stored globally (channel_id = NULL); user-owned personal data, not channel-scoped. /// Content is NIP-44 encrypted to the user's own keypair. pub const KIND_READ_STATE: u32 = 30078; /// NIP-42 auth event — never stored (carries bearer tokens). pub const KIND_AUTH: u32 = 22242; /// BUD-01: Blossom upload auth (used in upload.rs, not stored). pub const KIND_BLOSSOM_AUTH: u32 = 24242; /// Buzz custom one-time identity binding proof (ephemeral, not stored). pub const KIND_NOSTR_IDENTITY_BINDING: u32 = 24243; /// NIP-98: HTTP auth event (used in nip98.rs, not stored). pub const KIND_HTTP_AUTH: u32 = 27235; // NEW: Buzz command kinds (Pure Nostr plan) /// Agent metadata + owner reference (replaceable, agent-authored). pub const KIND_AGENT_PROFILE: u32 = 10100; /// NIP-AE: Agent Engram (parameterized replaceable, agent-authored). /// /// Encrypted memory record for AI agents. Addressed by `(pubkey_a, kind, d_tag)`, /// where `d_tag` is an HMAC over the agent↔owner conversation key. See /// `docs/nips/NIP-AE.md` and [`crate::engram`]. pub const KIND_AGENT_ENGRAM: u32 = 30174; /// NIP-ER: Event Reminder (parameterized replaceable, author-only). /// /// Encrypted, author-only reminder addressed by `(pubkey, kind, d_tag)`. The /// public `not_before` tag tells supporting relays when the reminder is due; /// the target, note, and state are NIP-44 encrypted to the author. Reads are /// author-only (see [`AUTHOR_ONLY_KINDS`]). See `docs/nips/NIP-ER.md`. pub const KIND_EVENT_REMINDER: u32 = 30300; /// NIP-PL: encrypted push lease (parameterized replaceable, author-only). /// /// The source event contains endpoint-bearing NIP-44 ciphertext and is readable /// only by its authenticated author. Effective delivery state lives in the /// dedicated push lease tables. pub const KIND_PUSH_LEASE: u32 = 30350; /// Kinds whose stored events are readable only by their author. /// /// The relay must never reveal the existence, count, tags, content, schedule, /// or search matches of these events to anyone but the authenticated author. /// Shared across the ingest write path (NIP-ER `not_before` validation) and the /// read path (REQ/COUNT/subscription author-only filtering). /// /// Currently a tiny linear set. If this grows past ~4 kinds, convert to a /// compile-time bitset or sorted array with binary search for hot-path use. pub const AUTHOR_ONLY_KINDS: &[u32] = &[KIND_EVENT_REMINDER, KIND_PUSH_LEASE]; /// Kinds that require a result-level read gate beyond the filter-layer /// `#p` check: even a reader who knows an event id MUST match the event's /// `#p` tag to receive the event. This closes the kindless `{ids:[…]}` read /// path for events whose existence must not be leaked. /// /// Used by `filter_can_match_result_gated_kinds` to force the per-event /// fallback path in COUNT rather than the fast SQL `count_events()`. pub const RESULT_GATED_KINDS: &[u32] = &[KIND_DM_VISIBILITY, KIND_AGENT_TURN_METRIC]; /// Kinds whose stored events have `#p`-bound read access — readable only by /// subscribers whose pubkey appears in the event's `#p` tag. /// /// The relay enforces this at the filter layer (`p_gated_filters_authorized`): /// a REQ that can match any kind in this set is closed unless the filter's /// `#p` values exactly equal the authenticated reader's pubkey. For stored /// (non-ephemeral) kinds in this set, the storage layer additionally writes a /// NULL `search_tsv` so the event is unsearchable through NIP-50 FTS /// (`schema/schema.sql` and `migrations/0001_initial_schema.sql` — drift /// caught by `p_gated_persistent_kinds_have_storage_null_tsvector` in /// `crates/buzz-search/tests/fts_integration.rs`). /// /// Ephemeral kinds (20000–29999, e.g. [`KIND_AGENT_OBSERVER_FRAME`]) are /// included for filter-layer enforcement but are never stored, so the /// storage-layer search defense does not apply to them. pub const P_GATED_KINDS: &[u32] = &[ KIND_AGENT_OBSERVER_FRAME, KIND_MEMBER_ADDED_NOTIFICATION, KIND_MEMBER_REMOVED_NOTIFICATION, KIND_GIFT_WRAP, KIND_DM_VISIBILITY, // NIP-AM: agent turn metrics are encrypted to the owner and must not be // readable by any unauthenticated or non-owner party, including via `ids` // filters — see NIP-AM §Relay Behavior. KIND_AGENT_TURN_METRIC, ]; /// NIP-AP: Agent Persona (parameterized replaceable, owner-authored). /// /// Persona definition event published by the workspace owner. Addressed by /// `(pubkey, kind, d_tag)` where `d_tag` is the plaintext persona slug. /// Content is a JSON body containing persona fields (system_prompt, /// display_name, avatar_url, runtime, model, provider, name_pool). /// Designed for discoverability and sharing — d-tag is not blinded. pub const KIND_PERSONA: u32 = 30175; /// NIP-AP: Agent Team (parameterized replaceable, owner-authored). /// /// Team definition event published by the workspace owner. Addressed by /// `(pubkey, kind, d_tag)` where `d_tag` is the team's stable id. Content is a /// JSON body projecting public team fields (name, description, persona_ids). /// A team is a user-facing grouping of personas; publishing keeps it /// authoritative across clients and reboots, mirroring `KIND_PERSONA`. pub const KIND_TEAM: u32 = 30176; /// NIP-AP: Managed Agent (parameterized replaceable, owner-authored). /// /// Managed-agent definition event published by the workspace owner. Addressed /// by `(pubkey, kind, d_tag)` where `d_tag` is the agent's pubkey. Content is /// an explicit opt-IN allowlist projection of the agent record — it MUST never /// carry the agent's secret key, NIP-OA auth tag, env vars, or runtime fields, /// since these events are world-readable on the relay. pub const KIND_MANAGED_AGENT: u32 = 30177; // NIP-56 reporting /// NIP-56: Report an event, pubkey, or blob to relay moderators (kind:1984). /// /// Accepted at ingest, persisted to the tenant-scoped `moderation_reports` /// queue, and never fanned out publicly. Reports are signals, not triggers: /// the relay never auto-actions on them (NIP-56). pub const KIND_REPORT: u32 = 1984; /// Buzz product feedback submission. Accepted at ingest, sidecarred to the /// deployment feedback table, and never stored or fanned out as an event. pub const KIND_PRODUCT_FEEDBACK: u32 = 42000; // NIP-29 group admin events /// NIP-29: Add a user to a group. pub const KIND_NIP29_PUT_USER: u32 = 9000; /// NIP-29: Remove a user from a group. pub const KIND_NIP29_REMOVE_USER: u32 = 9001; /// NIP-29: Edit group metadata. pub const KIND_NIP29_EDIT_METADATA: u32 = 9002; /// NIP-29: Delete an event from a group. pub const KIND_NIP29_DELETE_EVENT: u32 = 9005; /// NIP-29: Create a new group. pub const KIND_NIP29_CREATE_GROUP: u32 = 9007; /// NIP-29: Delete a group. pub const KIND_NIP29_DELETE_GROUP: u32 = 9008; /// NIP-29: Create an invite to a group. pub const KIND_NIP29_CREATE_INVITE: u32 = 9009; /// NIP-29: Request to join a group. pub const KIND_NIP29_JOIN_REQUEST: u32 = 9021; /// NIP-29: Request to leave a group. pub const KIND_NIP29_LEAVE_REQUEST: u32 = 9022; // Buzz community moderation commands (mod-signed, processed like 9030-series: // validated + executed directly, never stored as regular events; every // accepted command writes a `moderation_actions` audit row). /// Moderation: ban a pubkey from the community (`p` tag target, optional /// `expiration` + `reason` tags). pub const KIND_MODERATION_BAN: u32 = 9040; /// Moderation: lift a community ban (`p` tag target). pub const KIND_MODERATION_UNBAN: u32 = 9041; /// Moderation: timeout (write-block) a pubkey until an `expiration` tag /// timestamp (`p` tag target, optional `reason`). pub const KIND_MODERATION_TIMEOUT: u32 = 9042; /// Moderation: clear a timeout early (`p` tag target). pub const KIND_MODERATION_UNTIMEOUT: u32 = 9043; /// Moderation: resolve a report (`report` tag = report event id hex, /// `status` tag = resolved|dismissed, `action` tag = /// delete|kick|ban|timeout|dismiss|escalate — see /// `handlers/moderation_commands.rs` for the pinned vocabulary). pub const KIND_MODERATION_RESOLVE_REPORT: u32 = 9044; /// Returns `true` for community moderation command kinds (9040–9044). /// /// The canonical route check — use this instead of scattering /// `9040..=9044` matches across ingest/dispatch. pub const fn is_moderation_command_kind(kind: u32) -> bool { matches!( kind, KIND_MODERATION_BAN | KIND_MODERATION_UNBAN | KIND_MODERATION_TIMEOUT | KIND_MODERATION_UNTIMEOUT | KIND_MODERATION_RESOLVE_REPORT ) } // NIP-43 relay membership admin commands /// NIP-43: Add a pubkey to the relay member list. pub const RELAY_ADMIN_ADD_MEMBER: u32 = 9030; /// NIP-43: Remove a pubkey from the relay member list. pub const RELAY_ADMIN_REMOVE_MEMBER: u32 = 9031; /// NIP-43: Change the role of an existing relay member. pub const RELAY_ADMIN_CHANGE_ROLE: u32 = 9032; /// Buzz: Set the workspace profile (icon). Admin/owner-signed command. pub const RELAY_ADMIN_SET_WORKSPACE_PROFILE: u32 = 9033; // NIP-43 relay membership announcement events (relay-signed) /// NIP-43: Relay membership list snapshot (relay-signed, replaceable by convention). pub const KIND_NIP43_MEMBERSHIP_LIST: u32 = 13534; /// NIP-43: Member added announcement (relay-signed). pub const KIND_NIP43_MEMBER_ADDED: u32 = 8000; /// NIP-43: Member removed announcement (relay-signed). pub const KIND_NIP43_MEMBER_REMOVED: u32 = 8001; /// NIP-43: User leave request (user-signed, ephemeral). pub const KIND_NIP43_LEAVE_REQUEST: u32 = 28936; // NIP-IA identity archival requests (user/agent/owner-signed) /// NIP-IA: Request that the relay archive a target identity. pub const KIND_IA_ARCHIVE_REQUEST: u32 = 9035; /// NIP-IA: Request that the relay unarchive a target identity. pub const KIND_IA_UNARCHIVE_REQUEST: u32 = 9036; // NIP-IA identity archival announcement events (relay-signed) /// NIP-IA: Archived-identity delta (relay-signed). pub const KIND_IA_ARCHIVED: u32 = 8002; /// NIP-IA: Unarchived-identity delta (relay-signed). pub const KIND_IA_UNARCHIVED: u32 = 8003; /// NIP-IA: Archived identities list snapshot (relay-signed, replaceable). pub const KIND_IA_ARCHIVED_LIST: u32 = 13535; // NIP-29 group state (addressable range 39000–39003) /// NIP-29: Addressable group metadata state. pub const KIND_NIP29_GROUP_METADATA: u32 = 39000; /// NIP-29: Addressable group admins list. pub const KIND_NIP29_GROUP_ADMINS: u32 = 39001; /// NIP-29: Addressable group members list. pub const KIND_NIP29_GROUP_MEMBERS: u32 = 39002; /// NIP-29: Addressable group roles definition. pub const KIND_NIP29_GROUP_ROLES: u32 = 39003; // Channel-window overlays (relay-signed, synthesized at query time, never // stored). Appended to bridge `/query` responses for `top_level` window // requests — see docs/bridge-channel-window.md. /// Thread summary overlay: `e`/`d` tag = root event id, content = /// `{reply_count, descendant_count, last_reply_at, participants}`. pub const KIND_THREAD_SUMMARY: u32 = 39005; /// Window bounds overlay: `d` tag = `:`, /// content = `{has_more, next_cursor}`. The only authority on exhaustion — /// clients must not infer `has_more` from row counts. pub const KIND_WINDOW_BOUNDS: u32 = 39006; /// Workflow definition (parameterized replaceable, d=workflow_uuid). pub const KIND_WORKFLOW_DEF: u32 = 30620; /// NIP-DV: per-viewer DM visibility snapshot (relay-signed, parameterized /// replaceable, d=viewer_pubkey). Carries one `h` tag per DM the viewer has /// hidden from their sidebar. Re-published by the relay on every hide/unhide so /// the latest event is always the authoritative hidden set. The relay knows /// `hidden_at` per viewer; this is the only Nostr-visible projection of it. pub const KIND_DM_VISIBILITY: u32 = 30622; /// Lower bound of the NIP-33 parameterized replaceable range (30000–39999). pub const PARAM_REPLACEABLE_KIND_MIN: u32 = 30000; /// Upper bound of the NIP-33 parameterized replaceable range (30000–39999). pub const PARAM_REPLACEABLE_KIND_MAX: u32 = 39999; /// Lower bound of the ephemeral event range (20000–29999). Never stored. pub const EPHEMERAL_KIND_MIN: u32 = 20000; /// Upper bound of the ephemeral event range (20000–29999). Never stored. pub const EPHEMERAL_KIND_MAX: u32 = 29999; // Ephemeral events (20000–29999) — Redis pub/sub only, never stored. /// Ephemeral: user presence update (online/away/offline). pub const KIND_PRESENCE_UPDATE: u32 = 20001; /// NIP-AB: Device pairing event. Ephemeral — relay may discard after delivery. pub const KIND_PAIRING: u32 = 24134; /// Ephemeral: typing indicator for a channel. pub const KIND_TYPING_INDICATOR: u32 = 20002; /// Ephemeral: owner-scoped encrypted agent observer telemetry and control frame. pub const KIND_AGENT_OBSERVER_FRAME: u32 = 24200; /// Ephemeral: huddle emoji reaction burst. Channel-scoped to the ephemeral /// huddle channel with an `h` tag; never stored in the timeline. pub const KIND_HUDDLE_REACTION: u32 = 24810; // Stream messaging /// NIP-29 group chat message kind. V1 used kind:10001 (replaceable range — wrong), then 40001. /// /// Agent shutdown convention: the agent's owner sends a kind:9 message with content /// `"!shutdown"` and a `#p` tag mentioning the agent. The harness exits gracefully. /// This is a convention, not a new event kind — uses regular stream messages. pub const KIND_STREAM_MESSAGE: u32 = 9; /// V1 used kind:10002 (replaceable range — wrong). pub const KIND_STREAM_MESSAGE_V2: u32 = 40002; /// V1 used kind:10004 (replaceable range + NIP-51 collision — wrong). pub const KIND_STREAM_MESSAGE_EDIT: u32 = 40003; /// A stream message that has been pinned in a channel. pub const KIND_STREAM_MESSAGE_PINNED: u32 = 40004; /// A stream message that has been bookmarked by a user. pub const KIND_STREAM_MESSAGE_BOOKMARKED: u32 = 40005; /// A stream message scheduled for future delivery. pub const KIND_STREAM_MESSAGE_SCHEDULED: u32 = 40006; /// A reminder attached to a stream message or time. pub const KIND_STREAM_REMINDER: u32 = 40007; /// A diff/patch message showing file changes (unified diff format). pub const KIND_STREAM_MESSAGE_DIFF: u32 = 40008; /// Canvas (shared document) for a channel. pub const KIND_CANVAS: u32 = 40100; /// System message for channel state changes (join, leave, rename, etc.). pub const KIND_SYSTEM_MESSAGE: u32 = 40099; // Relay-only sidecar kinds (never client-submitted) /// Channel metadata with computed fields (relay-signed sidecar). pub const KIND_CHANNEL_SUMMARY: u32 = 40901; /// Bulk presence state (relay-signed sidecar). pub const KIND_PRESENCE_SNAPSHOT: u32 = 40902; // Direct messages (41000–41999) /// Open/create DM (p-tags = participants). pub const KIND_DM_OPEN: u32 = 41010; /// Add member to group DM. pub const KIND_DM_ADD_MEMBER: u32 = 41011; /// Hide DM from sidebar. pub const KIND_DM_HIDE: u32 = 41012; /// A new direct-message conversation was created. pub const KIND_DM_CREATED: u32 = 41001; // Agent job protocol (43000–43999) // Not using NIP-90 kinds (5000–6999) — Buzz requires auth chains (depth ≤ 3, breadth ≤ 10). /// An agent job was requested. pub const KIND_JOB_REQUEST: u32 = 43001; /// An agent accepted a job request. pub const KIND_JOB_ACCEPTED: u32 = 43002; /// Progress update for an in-flight agent job. pub const KIND_JOB_PROGRESS: u32 = 43003; /// Final result of a completed agent job. pub const KIND_JOB_RESULT: u32 = 43004; /// A job cancellation was requested. pub const KIND_JOB_CANCEL: u32 = 43005; /// An agent job failed with an error. pub const KIND_JOB_ERROR: u32 = 43006; /// Relay-signed notification: the target pubkey was added to a channel. /// Stored globally (channel_id = None) with p-tag = target, h-tag = channel UUID. pub const KIND_MEMBER_ADDED_NOTIFICATION: u32 = 44100; /// Relay-signed notification: the target pubkey was removed from a channel. /// Stored globally (channel_id = None) with p-tag = target, h-tag = channel UUID. pub const KIND_MEMBER_REMOVED_NOTIFICATION: u32 = 44101; /// NIP-AM: Agent Turn Metric — durable per-turn token-usage record (agent-authored). /// /// Regular stored event (append-only, never replaced). The agent publishes one /// event per completed turn, NIP-44 encrypted to its owner. Tags: exactly one `p` /// (owner pubkey) and one `agent` (agent pubkey == event pubkey); no `h` tag. /// Stored globally (channel_id = NULL); owner-scoped reads only (p-gated, NIP-42). /// See `docs/nips/NIP-AM.md`. pub const KIND_AGENT_TURN_METRIC: u32 = 44200; // Forum / social (45000–45999) // V1 used addressable range (30001–30003) — wrong. /// A forum post (thread root). pub const KIND_FORUM_POST: u32 = 45001; /// A vote on a forum post. pub const KIND_FORUM_VOTE: u32 = 45002; /// A comment reply on a forum post. pub const KIND_FORUM_COMMENT: u32 = 45003; // Workflow engine (46000–46999) /// Trigger workflow execution. pub const KIND_WORKFLOW_TRIGGER: u32 = 46020; /// Grant pending approval. pub const KIND_APPROVAL_GRANT: u32 = 46030; /// Deny pending approval. pub const KIND_APPROVAL_DENY: u32 = 46031; /// A workflow was triggered by a matching event. pub const KIND_WORKFLOW_TRIGGERED: u32 = 46001; /// A workflow step began execution. pub const KIND_WORKFLOW_STEP_STARTED: u32 = 46002; /// A workflow step completed successfully. pub const KIND_WORKFLOW_STEP_COMPLETED: u32 = 46003; /// A workflow step failed. pub const KIND_WORKFLOW_STEP_FAILED: u32 = 46004; /// The entire workflow completed successfully. pub const KIND_WORKFLOW_COMPLETED: u32 = 46005; /// The entire workflow failed. pub const KIND_WORKFLOW_FAILED: u32 = 46006; /// The workflow was cancelled before completion. pub const KIND_WORKFLOW_CANCELLED: u32 = 46007; /// A workflow step is waiting for human approval. pub const KIND_WORKFLOW_APPROVAL_REQUESTED: u32 = 46010; /// A pending workflow approval was granted. pub const KIND_WORKFLOW_APPROVAL_GRANTED: u32 = 46011; /// A pending workflow approval was denied. pub const KIND_WORKFLOW_APPROVAL_DENIED: u32 = 46012; // User groups (47000–47999) // System / admin custom range (48000–48999) /// An audit log entry was recorded. pub const KIND_AUDIT_ENTRY: u32 = 48001; /// A huddle (audio/video session) was started. pub const KIND_HUDDLE_STARTED: u32 = 48100; /// A participant joined a huddle. pub const KIND_HUDDLE_PARTICIPANT_JOINED: u32 = 48101; /// A participant left a huddle. pub const KIND_HUDDLE_PARTICIPANT_LEFT: u32 = 48102; /// A huddle ended. pub const KIND_HUDDLE_ENDED: u32 = 48103; /// Huddle channel guidelines/rules document. pub const KIND_HUDDLE_GUIDELINES: u32 = 48106; // Media (49000–49999) /// Internal kind for media upload audit entries. Not a relay event kind. pub const KIND_MEDIA_UPLOAD: u32 = 49001; /// NIP-34: Repository announcement (parameterized replaceable, d-tag = repo-id). pub const KIND_GIT_REPO_ANNOUNCEMENT: u32 = 30617; /// NIP-34: Repository state — current branch/tag refs (parameterized replaceable, d-tag = repo-id). pub const KIND_GIT_REPO_STATE: u32 = 30618; /// NIP-34: Patch (git format-patch output). pub const KIND_GIT_PATCH: u32 = 1617; /// NIP-34: Pull request. pub const KIND_GIT_PULL_REQUEST: u32 = 1618; /// NIP-34: Pull request update (tip commit change). pub const KIND_GIT_PR_UPDATE: u32 = 1619; /// NIP-34: Issue. pub const KIND_GIT_ISSUE: u32 = 1621; /// NIP-34: Status — Open. pub const KIND_GIT_STATUS_OPEN: u32 = 1630; /// NIP-34: Status — Applied / Merged. pub const KIND_GIT_STATUS_MERGED: u32 = 1631; /// NIP-34: Status — Closed. pub const KIND_GIT_STATUS_CLOSED: u32 = 1632; /// NIP-34: Status — Draft. pub const KIND_GIT_STATUS_DRAFT: u32 = 1633; /// All registered kind constants — used for duplicate detection and iteration. pub const ALL_KINDS: &[u32] = &[ KIND_PROFILE, KIND_TEXT_NOTE, KIND_CONTACT_LIST, KIND_MUTE_LIST, KIND_PIN_LIST, KIND_NIP65_RELAY_LIST_METADATA, KIND_BOOKMARK_LIST, KIND_EMOJI_LIST, KIND_FOLLOW_SET, KIND_BOOKMARK_SET, KIND_EMOJI_SET, KIND_CHANNEL_METADATA, KIND_DELETION, KIND_REACTION, KIND_GIFT_WRAP, KIND_FILE_METADATA, KIND_AGENT_PROFILE, KIND_AGENT_ENGRAM, KIND_EVENT_REMINDER, KIND_PERSONA, KIND_TEAM, KIND_MANAGED_AGENT, KIND_REPORT, KIND_PRODUCT_FEEDBACK, KIND_NIP29_PUT_USER, KIND_NIP29_REMOVE_USER, KIND_NIP29_EDIT_METADATA, KIND_NIP29_DELETE_EVENT, KIND_NIP29_CREATE_GROUP, KIND_NIP29_DELETE_GROUP, KIND_NIP29_CREATE_INVITE, KIND_NIP29_JOIN_REQUEST, KIND_NIP29_LEAVE_REQUEST, KIND_MODERATION_BAN, KIND_MODERATION_UNBAN, KIND_MODERATION_TIMEOUT, KIND_MODERATION_UNTIMEOUT, KIND_MODERATION_RESOLVE_REPORT, RELAY_ADMIN_ADD_MEMBER, RELAY_ADMIN_REMOVE_MEMBER, RELAY_ADMIN_CHANGE_ROLE, RELAY_ADMIN_SET_WORKSPACE_PROFILE, KIND_NIP43_MEMBERSHIP_LIST, KIND_NIP43_MEMBER_ADDED, KIND_NIP43_MEMBER_REMOVED, KIND_NIP43_LEAVE_REQUEST, KIND_IA_ARCHIVE_REQUEST, KIND_IA_UNARCHIVE_REQUEST, KIND_IA_ARCHIVED, KIND_IA_UNARCHIVED, KIND_IA_ARCHIVED_LIST, KIND_NIP29_GROUP_METADATA, KIND_NIP29_GROUP_ADMINS, KIND_NIP29_GROUP_MEMBERS, KIND_NIP29_GROUP_ROLES, KIND_THREAD_SUMMARY, KIND_WINDOW_BOUNDS, KIND_PRESENCE_UPDATE, KIND_TYPING_INDICATOR, KIND_HUDDLE_REACTION, KIND_BLOSSOM_AUTH, KIND_PAIRING, KIND_AGENT_OBSERVER_FRAME, KIND_HTTP_AUTH, KIND_STREAM_MESSAGE, KIND_STREAM_MESSAGE_V2, KIND_STREAM_MESSAGE_EDIT, KIND_STREAM_MESSAGE_PINNED, KIND_STREAM_MESSAGE_BOOKMARKED, KIND_STREAM_MESSAGE_SCHEDULED, KIND_STREAM_REMINDER, KIND_STREAM_MESSAGE_DIFF, KIND_CANVAS, KIND_SYSTEM_MESSAGE, KIND_CHANNEL_SUMMARY, KIND_PRESENCE_SNAPSHOT, KIND_DM_VISIBILITY, KIND_DM_OPEN, KIND_DM_ADD_MEMBER, KIND_DM_HIDE, KIND_DM_CREATED, KIND_JOB_REQUEST, KIND_JOB_ACCEPTED, KIND_JOB_PROGRESS, KIND_JOB_RESULT, KIND_JOB_CANCEL, KIND_JOB_ERROR, KIND_MEMBER_ADDED_NOTIFICATION, KIND_MEMBER_REMOVED_NOTIFICATION, KIND_AGENT_TURN_METRIC, KIND_WORKFLOW_DEF, KIND_LONG_FORM, KIND_USER_STATUS, KIND_READ_STATE, KIND_FORUM_POST, KIND_FORUM_VOTE, KIND_FORUM_COMMENT, KIND_WORKFLOW_TRIGGER, KIND_APPROVAL_GRANT, KIND_APPROVAL_DENY, KIND_WORKFLOW_TRIGGERED, KIND_WORKFLOW_STEP_STARTED, KIND_WORKFLOW_STEP_COMPLETED, KIND_WORKFLOW_STEP_FAILED, KIND_WORKFLOW_COMPLETED, KIND_WORKFLOW_FAILED, KIND_WORKFLOW_CANCELLED, KIND_WORKFLOW_APPROVAL_REQUESTED, KIND_WORKFLOW_APPROVAL_GRANTED, KIND_WORKFLOW_APPROVAL_DENIED, KIND_AUDIT_ENTRY, KIND_HUDDLE_STARTED, KIND_HUDDLE_PARTICIPANT_JOINED, KIND_HUDDLE_PARTICIPANT_LEFT, KIND_HUDDLE_ENDED, KIND_HUDDLE_GUIDELINES, KIND_MEDIA_UPLOAD, KIND_GIT_REPO_ANNOUNCEMENT, KIND_GIT_REPO_STATE, KIND_GIT_PATCH, KIND_GIT_PULL_REQUEST, KIND_GIT_PR_UPDATE, KIND_GIT_ISSUE, KIND_GIT_STATUS_OPEN, KIND_GIT_STATUS_MERGED, KIND_GIT_STATUS_CLOSED, KIND_GIT_STATUS_DRAFT, ]; /// Returns `true` if `kind` is in the ephemeral range (20000–29999). pub const fn is_ephemeral(kind: u32) -> bool { kind >= EPHEMERAL_KIND_MIN && kind <= EPHEMERAL_KIND_MAX } /// Returns `true` if `kind` is replaceable (NIP-01: kinds 0, 3, 41, 10000–19999). /// NIP-33 parameterized-replaceable kinds (30000–39999) use a different replacement /// key (includes `d`-tag) and are handled separately via `replace_parameterized_event`. pub const fn is_replaceable(kind: u32) -> bool { matches!(kind, 0 | 3 | KIND_CHANNEL_METADATA | 10000..=19999) } /// Returns `true` if `kind` is in the NIP-33 parameterized replaceable range (30000–39999). /// /// These events are keyed by `(pubkey, kind, d_tag)` — the latest `created_at` wins. pub const fn is_parameterized_replaceable(kind: u32) -> bool { kind >= PARAM_REPLACEABLE_KIND_MIN && kind <= PARAM_REPLACEABLE_KIND_MAX } /// Returns `true` if `kind` is a workflow execution event (46001–46012). /// These must not trigger workflows (prevents infinite loops). pub const fn is_workflow_execution_kind(kind: u32) -> bool { kind >= KIND_WORKFLOW_TRIGGERED && kind <= KIND_WORKFLOW_APPROVAL_DENIED } /// Returns `true` if `kind` is a NIP-43 relay membership admin command (9030–9032) /// or the Buzz workspace-profile admin command (9033). pub const fn is_relay_admin_kind(kind: u32) -> bool { matches!( kind, RELAY_ADMIN_ADD_MEMBER | RELAY_ADMIN_REMOVE_MEMBER | RELAY_ADMIN_CHANGE_ROLE | RELAY_ADMIN_SET_WORKSPACE_PROFILE ) } /// Returns `true` if `kind` is a NIP-IA identity archival request (9035–9036). /// /// Only the user-signed *request* kinds are matched. The relay-signed delta and /// snapshot kinds (8002/8003/13535) are emitted by the relay, never ingested as /// commands, so they are intentionally excluded. pub const fn is_identity_archive_request_kind(kind: u32) -> bool { matches!(kind, KIND_IA_ARCHIVE_REQUEST | KIND_IA_UNARCHIVE_REQUEST) } /// Returns `true` if `kind` is a Buzz command kind that requires transactional execution. pub const fn is_command_kind(kind: u32) -> bool { matches!( kind, KIND_WORKFLOW_DEF | KIND_DM_OPEN | KIND_DM_ADD_MEMBER | KIND_DM_HIDE | KIND_WORKFLOW_TRIGGER | KIND_APPROVAL_GRANT | KIND_APPROVAL_DENY ) } /// Returns `true` if `kind` is a relay-only sidecar kind. /// Client submission of these kinds must be rejected. pub const fn is_relay_only_kind(kind: u32) -> bool { matches!( kind, KIND_CHANNEL_SUMMARY | KIND_PRESENCE_SNAPSHOT | KIND_DM_VISIBILITY | KIND_THREAD_SUMMARY | KIND_WINDOW_BOUNDS ) } /// Extract the kind from a nostr Event as u32. /// NIP-01 specifies kind as an unsigned integer; u32 covers the full range. pub fn event_kind_u32(event: &nostr::Event) -> u32 { event.kind.as_u16() as u32 } /// Extract the kind from a nostr Event as i32 (for Postgres INT columns). /// Safe: all Buzz kinds fit in i32 (max 65535 < i32::MAX). pub fn event_kind_i32(event: &nostr::Event) -> i32 { event.kind.as_u16() as i32 } // Compile-time: new kinds are in the expected ranges. const _: () = assert!(is_replaceable(KIND_AGENT_PROFILE)); // 10100 ∈ 10000–19999 const _: () = assert!(is_parameterized_replaceable(KIND_PERSONA)); // 30175 ∈ 30000–39999 const _: () = assert!(is_parameterized_replaceable(KIND_TEAM)); // 30176 ∈ 30000–39999 const _: () = assert!(is_parameterized_replaceable(KIND_MANAGED_AGENT)); // 30177 ∈ 30000–39999 const _: () = assert!(is_parameterized_replaceable(KIND_WORKFLOW_DEF)); // 30620 ∈ 30000–39999 const _: () = assert!(is_parameterized_replaceable(KIND_EVENT_REMINDER)); // 30300 ∈ 30000–39999 const _: () = assert!(is_parameterized_replaceable(KIND_DM_VISIBILITY)); // 30622 ∈ 30000–39999 const _: () = assert!(is_parameterized_replaceable(KIND_THREAD_SUMMARY)); // 39005 ∈ 30000–39999 const _: () = assert!(is_parameterized_replaceable(KIND_WINDOW_BOUNDS)); // 39006 ∈ 30000–39999 // Compile-time: NIP-34 parameterized replaceable kinds are in the correct range. const _: () = assert!( KIND_GIT_REPO_ANNOUNCEMENT >= PARAM_REPLACEABLE_KIND_MIN && KIND_GIT_REPO_ANNOUNCEMENT <= PARAM_REPLACEABLE_KIND_MAX ); const _: () = assert!( KIND_GIT_REPO_STATE >= PARAM_REPLACEABLE_KIND_MIN && KIND_GIT_REPO_STATE <= PARAM_REPLACEABLE_KIND_MAX ); // Compile-time: all Buzz kind constants fit in nostr's u16-backed Kind. const _: () = assert!(KIND_AUTH <= u16::MAX as u32); const _: () = assert!(KIND_CANVAS <= u16::MAX as u32); const _: () = assert!(KIND_HUDDLE_GUIDELINES <= u16::MAX as u32); const _: () = assert!(EPHEMERAL_KIND_MIN < EPHEMERAL_KIND_MAX); // Compile-time: KIND_AGENT_TURN_METRIC is a regular stored kind (not ephemeral, not replaceable). const _: () = assert!(!is_ephemeral(KIND_AGENT_TURN_METRIC)); const _: () = assert!(!is_replaceable(KIND_AGENT_TURN_METRIC)); const _: () = assert!(!is_parameterized_replaceable(KIND_AGENT_TURN_METRIC)); const _: () = assert!(KIND_AGENT_TURN_METRIC <= u16::MAX as u32); // Moderation kinds fit u16 and are neither replaceable nor ephemeral: // 1984 is a regular event (persisted to the queue, never fanned out); // 9040–9044 are direct commands (executed, never stored). const _: () = assert!(KIND_REPORT <= u16::MAX as u32); const _: () = assert!(KIND_MODERATION_RESOLVE_REPORT <= u16::MAX as u32); const _: () = assert!(!is_ephemeral(KIND_REPORT)); const _: () = assert!(is_moderation_command_kind(KIND_MODERATION_BAN)); const _: () = assert!(is_moderation_command_kind(KIND_MODERATION_RESOLVE_REPORT)); const _: () = assert!(!is_moderation_command_kind(KIND_REPORT)); #[cfg(test)] mod tests { use super::*; #[test] fn no_duplicate_kind_values() { let mut seen = std::collections::HashSet::new(); for &k in ALL_KINDS { assert!(seen.insert(k), "duplicate kind value: {k}"); } } #[test] fn parameterized_replaceable_range() { assert!(!is_parameterized_replaceable(29999)); assert!(is_parameterized_replaceable(30000)); assert!(is_parameterized_replaceable(30023)); // NIP-23 long-form assert!(is_parameterized_replaceable(39000)); // NIP-29 group metadata assert!(is_parameterized_replaceable(39999)); assert!(!is_parameterized_replaceable(40000)); } #[test] fn replaceable_and_parameterized_are_disjoint() { for kind in 0..=65535u32 { assert!( !(is_replaceable(kind) && is_parameterized_replaceable(kind)), "kind {kind} is both replaceable and parameterized replaceable" ); } } }