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

> Learn how to register event kind numbers and integrate them into the Buzz ingest pipeline. Discover the process for validating and handling new event kinds.

- Repository: [Block Open Source/buzz](https://github.com/block/buzz)
- Tags: how-to-guide
- Published: 2026-08-29

---

**Buzz registers all Nostr event kind numbers as public constants in [`crates/buzz-core/src/kind.rs`](https://github.com/block/buzz/blob/main/crates/buzz-core/src/kind.rs), validates incoming events through the ingest handler in [`crates/buzz-relay/src/handlers/ingest.rs`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/kind.rs).

### Declare the Constant in kind.rs

Open [`crates/buzz-core/src/kind.rs`](https://github.com/block/buzz/blob/main/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:

```rust
/// 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`](https://github.com/block/buzz/blob/main/ARCHITECTURE.md)
- Document the semantics in [`CONTRIBUTING.md`](https://github.com/block/buzz/blob/main/CONTRIBUTING.md) following the process outlined in lines 417-424
- Update [`CHANGELOG.md`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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:

```rust
// 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`](https://github.com/block/buzz/blob/main/kind.rs):**

```rust
// 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:**

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

```

**Rust integration test verifying successful ingestion:**

```rust
#[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`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/CONTRIBUTING.md)** – Authoritative guide for adding new kinds (section "Register event kinds" around lines 417-424)
- **[`ARCHITECTURE.md`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/ARCHITECTURE.md) and [`CONTRIBUTING.md`](https://github.com/block/buzz/blob/main/CONTRIBUTING.md)
- Implement validation logic and side effects in [`crates/buzz-relay/src/handlers/ingest.rs`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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`](https://github.com/block/buzz/blob/main/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.

### What is the recommended range for custom Buzz-specific event kinds?

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.