How to Register Event Kind Numbers and Integrate Them into the Buzz Ingest Pipeline

Buzz registers all Nostr event kind numbers as public constants in crates/buzz-core/src/kind.rs, validates incoming events through the ingest handler in crates/buzz-relay/src/handlers/ingest.rs, and requires developers to declare new kinds in the appropriate numeric range before wiring validation logic and side effects into the pipeline.

The Buzz relay (block/buzz) treats every Nostr event as a typed "kind" that determines how the ingest pipeline processes, validates, and stores the data. Registering a new event kind requires updating the central kind registry, implementing validation rules, and ensuring the ingest handler routes the event correctly according to the patterns defined in CONTRIBUTING.md.

Understanding Buzz Event Kinds and the Ingest Pipeline

Buzz organizes Nostr events into distinct numeric kinds that control behavior throughout the system. The kind registry in crates/buzz-core/src/kind.rs defines these as pub const u32 values, grouping them by semantic purpose and mutability constraints. Standard Nostr kinds (0 for metadata, 1 for text notes) remain unchanged, while replaceable kinds occupy the 30000–39999 range and ephemeral kinds use 20000–29999.

The ingest pipeline lives in crates/buzz-relay/src/handlers/ingest.rs, where every incoming WebSocket or HTTP event passes through validation gates before database storage. This handler uses pattern matching on the kind field to route events to specific validate_* functions and handle_* arms that enforce payload constraints and trigger side effects like thread counter updates or auxiliary row creation.

Step-by-Step Process for Registering New Event Kinds

Adding a custom event kind to Buzz follows a strict workflow to maintain consistency across the registry, documentation, and validation layers.

Choose the Appropriate Kind Number Range

Select a numeric range that matches the event's lifecycle requirements. According to the source code comments and CONTRIBUTING.md (lines 417-424), the ranges break down as follows:

  • 0–9999 – Standard Nostr kinds (reserved, never change)
  • 20000–29999 – Ephemeral kinds for temporary, client-only events
  • 30000–39999 – Replaceable kinds for user-generated sets that supersede previous versions
  • 40000+ – Buzz-specific custom kinds for agent metrics, workflow definitions, and proprietary features

Pick a number in the appropriate range that does not conflict with existing constants in kind.rs.

Declare the Constant in kind.rs

Open crates/buzz-core/src/kind.rs and insert a new public constant in the correct numeric block. Include a doc comment that serves as the authoritative description:

/// Custom sticky-note events for persistent user reminders.
pub const KIND_STICKY_NOTE: u32 = 40200;

Place this constant adjacent to other Buzz-specific kinds (40000+) to maintain logical grouping. If the kind requires access control, add it to the SHARED_GATED_KINDS or RESULT_GATED_KINDS arrays defined in the same file.

Update Registry Documentation

Synchronize the human-readable documentation to reflect the new constant:

  • Add the kind to the auto-generated "Event kinds" table in ARCHITECTURE.md
  • Document the semantics in CONTRIBUTING.md following the process outlined in lines 417-424
  • Update CHANGELOG.md if the kind exposes new public API surface
  • Create or modify NIP specification files under docs/nips/ if applicable

Wire the Kind into the Ingest Handler

Navigate to crates/buzz-relay/src/handlers/ingest.rs to integrate validation and routing logic. The ingest handler automatically routes events based on their numeric kind field, but you must explicitly add:

  1. Validation logic in a validate_* function if the kind enforces specific payload constraints
  2. Processing logic in a handle_* match arm for side effects such as updating counters or creating auxiliary database rows
  3. ACL checks by referencing the kind constant in gated arrays if only specific principals may emit it

Implement Validation and Side Effects

For kinds requiring custom validation, extend the existing validation functions to check payload structure before the database transaction commits. For side effects, modify the handler arms to execute auxiliary operations atomically with the primary storage:

// Example pattern in ingest.rs match arm
KIND_STICKY_NOTE => {
    validate_sticky_note(&event)?;
    create_sticky_row(&event, &mut tx).await?;
}

Add Comprehensive Tests

Create unit and integration tests to verify the new kind flows through the pipeline correctly:

  • Add unit tests in crates/buzz-relay/tests/ that construct minimal Nostr JSON events
  • Include negative tests that verify malformed payloads trigger validation failures
  • Run the end-to-end test client (buzz-test-client) to exercise WebSocket and HTTP ingestion paths

Execute just ci locally to ensure formatting, linting, and the full test suite pass before submission.

Code Examples for Event Kind Registration

The following patterns demonstrate how to register and test a new event kind in the Buzz codebase.

Declaring the constant in kind.rs:

// crates/buzz-core/src/kind.rs
/// A custom event used by the "sticky-note" feature.
pub const KIND_STICKY_NOTE: u32 = 40200;

Constructing a minimal Nostr event for testing:

{
  "id": "e3b0c44298fc1c149afbf4c8996fb924",
  "pubkey": "02a1b2c3d4e5f6...",
  "created_at": 1700000000,
  "kind": 40200,
  "tags": [],
  "content": "Just a sticky note!",
  "sig": "3045022100..."
}

Rust integration test verifying successful ingestion:

#[tokio::test]
async fn ingest_sticky_note_is_accepted() {
    let client = test_client().await;      // from buzz-test-client
    let ev = json!({
        "kind": KIND_STICKY_NOTE,
        "content": "Demo note",
        "created_at": chrono::Utc::now().timestamp(),
        "tags": []
    });
    let resp = client.post_events(&ev).await.unwrap();
    assert!(resp.accepted, "Ingest rejected: {}", resp.message);
}

Key Files and Their Roles

Understanding the repository structure helps navigate the registration process efficiently:

  • crates/buzz-core/src/kind.rs – Central registry containing all pub const u32 kind declarations and gated kind arrays like SHARED_GATED_KINDS and RESULT_GATED_KINDS
  • crates/buzz-relay/src/handlers/ingest.rs – Core ingest gate that validates, routes, and applies side effects for every incoming event via validate_* and handle_* functions
  • CONTRIBUTING.md – Authoritative guide for adding new kinds (section "Register event kinds" around lines 417-424)
  • ARCHITECTURE.md – Human-readable overview of the current kind space and ingest architecture
  • crates/buzz-test-client/tests/ – Integration tests that hit the ingest pipeline via WebSocket and HTTP

Summary

Registering event kind numbers in Buzz requires coordination across the core library and relay handler:

  • Declare new kinds as pub const u32 values in crates/buzz-core/src/kind.rs within the appropriate numeric range (40000+ for Buzz-specific features)
  • Document the constant with descriptive comments and update ARCHITECTURE.md and CONTRIBUTING.md
  • Implement validation logic and side effects in crates/buzz-relay/src/handlers/ingest.rs by extending validate_* functions and handle_* match arms
  • Add the kind to SHARED_GATED_KINDS or RESULT_GATED_KINDS if access control is required
  • Write unit and end-to-end tests using buzz-test-client to verify successful ingestion and proper rejection of invalid payloads
  • Run just ci to validate changes before submitting a pull request

Frequently Asked Questions

What file contains all event kind constants in Buzz?

All event kind constants are centralized in crates/buzz-core/src/kind.rs. This file defines every kind as a pub const u32 and organizes them by numeric range, including standard Nostr kinds, ephemeral kinds, replaceable kinds, and Buzz-specific custom kinds starting at 40000.

How does the ingest pipeline validate new event kinds?

The ingest pipeline in crates/buzz-relay/src/handlers/ingest.rs validates events through dedicated validate_* functions and routes them via handle_* match arms based on the kind field. When you register a new kind, you must explicitly add validation logic to check payload constraints and implement side effects within these handler functions before the event reaches the database.

Buzz reserves the 40000+ range for custom, implementation-specific event kinds. Standard Nostr kinds use 0–9999, ephemeral kinds occupy 20000–29999, and replaceable kinds use 30000–39999. Always select a number in the 40000+ range for proprietary features to avoid conflicts with the Nostr protocol specification.

How do I test a newly registered event kind in Buzz?

Create integration tests in crates/buzz-relay/tests/ or use the buzz-test-client crate to construct minimal Nostr JSON events with your new kind number. Post these events via WebSocket or HTTP, then assert that the ingest pipeline accepts valid payloads and rejects malformed ones. Run just ci locally to ensure all tests pass before submitting your changes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →