How Buzz Handles NIP-17 Gift Wraps and NIP-44 Encrypted DMs in Nostr

Buzz implements NIP-17 gift-wrapped DMs through ephemeral-key signed events with strict p-tag filtering while maintaining NIP-44 encryption capabilities for other private payloads, though direct NIP-44 DMs remain unimplemented.

The block/buzz repository provides a relay-first Nostr client that treats privacy-enhancing direct message protocols with distinct architectural approaches. Understanding how Buzz handles NIP-17 gift wraps and NIP-44 encrypted DMs reveals important implementation patterns for developers building privacy-preserving messaging on Nostr.

NIP-17 Gift-Wrapped Direct Messages Implementation

Buzz treats NIP-17 gift-wraps as opaque envelopes that protect sender identity while ensuring targeted delivery.

Ephemeral Key Signing and Relay Verification

Gift-wrap events use kind:1059 and are signed with an ephemeral key rather than the author's permanent identity key. This allows the relay to verify event authenticity without learning the author's identity. In crates/buzz-relay/src/handlers/ingest.rs, the relay bypasses the normal author-pubkey check for these events, instead validating only the presence of a #p tag containing the recipient's public key.

P-Gate Filtering for Privacy

The relay enforces strict p-gate filtering to prevent metadata leakage. Subscriptions must include a matching #p filter to receive gift-wrap events. The implementation in crates/buzz-relay/src/handlers/req.rs contains the function p_gate_rejects_bare_kind_search_filter_for_gift_wrap, which explicitly rejects queries attempting to search gift-wraps without specifying a recipient pubkey.

Push runtime matching follows the same constraint. The function gift_wrap_match_requires_self_p_filter_and_recipient in crates/buzz-relay/src/push_runtime.rs ensures that a lease can only match gift-wraps addressed to its own author, preventing unauthorized interception.

Indexing and Search Restrictions

Gift-wraps receive no full-text indexing and remain invisible to NIP-50 search. The relay explicitly excludes these events from generic kind searches to prevent correlation attacks. End-to-end tests in crates/buzz-test-client/tests/e2e_nostr_interop.rs assert that created gift-wraps never appear in search results, confirming the privacy guarantee.

Sending NIP-17 Gift-Wraps

The following Rust code demonstrates creating and sending a gift-wrap event using the Buzz SDK:

use buzz_sdk::builders::EventBuilder;
use buzz_sdk::kind::Kind;

// Create a unique payload
let payload = format!("gift‑wrap‑{}", uuid::Uuid::new_v4());

// Build a gift-wrap event (kind 1059) addressed to recipient B
let gift_wrap = EventBuilder::new(Kind::Custom(1059), &payload)
    .add_tag(["p", recipient_pubkey])
    .expect("sign gift wrap");

// Send via a connected client
let ok = client.send_event(gift_wrap).await.expect("send gift wrap");
assert!(ok.accepted, "relay rejected gift wrap: {}", ok.message);

According to the source code at crates/buzz-test-client/tests/e2e_nostr_interop.rs (lines 602-725), this pattern successfully delivers events to intended recipients while maintaining sender anonymity.

NIP-44 Encryption Support and Current Limitations

While Buzz does not currently implement NIP-44 for direct messaging, the cryptographic infrastructure exists for other private payloads.

Where NIP-44 is Currently Used

Buzz utilizes NIP-44 v2 encryption extensively for non-DM private data:

  • Reminders: Events of kind:30300 (NIP-ER) encrypt reminder bodies using NIP-44
  • Agent memory: Events of kind:30174 (NIP-AE) encrypt engrams to the owner-agent conversation key
  • Desktop storage: Locked envelopes and persona cards in the Tauri side store use NIP-44 for encrypting sensitive snapshots and manifests

Mobile Crypto Implementation

The mobile crypto library at mobile/lib/shared/crypto/nip44.dart provides the complete NIP-44 v2 implementation. The encrypt function derives the conversation key via ECDH, then applies XChaCha20-Poly1305 authenticated encryption. The corresponding decrypt function verifies the version byte, HMAC, and payload length before returning plaintext.

Payload Limits and Specifications

NIP-44 payloads in Buzz are capped at 65,535 bytes of plaintext. The relay enforces a 256 KB total content limit, providing sufficient headroom for encryption overhead. Self-encryption patterns support "encrypt-to-self" use cases where the same key pair handles both encryption and decryption for personal data like reminders.

Encrypting with NIP-44 in Dart

The following Flutter/Dart example shows NIP-44 encryption for private payloads:

import 'package:buzz_shared/crypto/nip44.dart';

final privKey = Hex.decode('...'); // your private key
final pubKey  = Hex.decode('...'); // recipient's public key
final plaintext = utf8.encode('Hello, encrypted DM!');

// Derive conversation key & encrypt
final ciphertext = await NIP44.encrypt(privKey, pubKey, plaintext);

// Decrypt (recipient)
final decrypted = await NIP44.decrypt(pubKey, privKey, ciphertext);
assert(utf8.decode(decrypted) == 'Hello, encrypted DM!');

As documented in NOSTR.md, direct NIP-44 DM support remains on the roadmap, potentially as a new kind such as kind:10050.

Summary

  • NIP-17 gift-wraps (kind:1059) in Buzz use ephemeral keys and strict p-tag filtering to enable anonymous, targeted delivery without search indexing
  • The relay enforces privacy through p_gate_rejects_bare_kind_search_filter_for_gift_wrap and related functions that require recipient-specific subscriptions
  • NIP-44 encryption is fully implemented for reminders, agent memory, and storage, but not yet available for direct messaging between users
  • Mobile and desktop clients share the NIP-44 v2 implementation using XChaCha20-Poly1305 via mobile/lib/shared/crypto/nip44.dart
  • Executors must ignore encrypted payloads within gift-wraps and use only outer-envelope fields for matching, as specified in docs/nips/NIP-PL.md

Frequently Asked Questions

What is the difference between NIP-17 and NIP-44 in Buzz?

NIP-17 provides the transport mechanism for anonymous DMs using gift-wrap envelopes (kind:1059), while NIP-44 provides the encryption algorithm for message content. Buzz currently uses NIP-17 gift-wraps to deliver opaque payloads, which may contain NIP-44 encrypted content, but does not yet support native NIP-44 DMs as a standalone message type.

Why are NIP-17 gift-wraps not searchable in Buzz?

Buzz explicitly excludes gift-wraps from the NIP-50 full-text search index to prevent metadata correlation attacks. The end-to-end tests verify that gift-wraps never appear in search results, ensuring that only recipients with explicit knowledge of the event ID or real-time subscription filters can access these messages.

How does Buzz verify NIP-17 gift-wrap authenticity without revealing the sender?

The relay validates the cryptographic signature using an ephemeral key generated specifically for that event, rather than the author's permanent identity key. As implemented in crates/buzz-relay/src/handlers/ingest.rs, the relay skips the standard author-pubkey verification and instead validates only the structural integrity and recipient p-tag, allowing authentication without identity disclosure.

When will Buzz support native NIP-44 encrypted DMs?

According to NOSTR.md, NIP-44 encrypted DMs remain on the development roadmap, potentially implemented as a new kind (such as kind:10050). Until then, users seeking private messaging should rely on NIP-17 gift-wraps, which can optionally contain NIP-44 encrypted payloads for end-to-end encryption.

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 →