Skip to main content
Glama

Nostr MCP Server

by AustinKelsay
# NIP-19: Bech32-Encoded Entities This module implements [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md), which defines human-readable encodings for Nostr entities using the Bech32 format. ## Overview NIP-19 provides standardized human-friendly encodings for various Nostr entities like public keys, private keys, event IDs, and more complex structures like profiles, events, and addresses. These encodings use the Bech32 format which includes: - Human-readable prefixes (e.g., `npub`, `nsec`, `note`) - Error detection capabilities - Consistent formatting across different types of entities ## Key Features - 🔤 **Multiple Entity Types**: Support for all NIP-19 entities (npub, nsec, note, nprofile, nevent, naddr) - 🔄 **Bi-directional Conversion**: Encode and decode all entity types - ✅ **Validation**: Thorough validation for all entity formats - 💼 **TLV Handling**: Support for Type-Length-Value data in complex entities - 🛡️ **Error Handling**: Meaningful error messages for invalid inputs - ⚡ **Performance**: Optimized for speed and memory efficiency ## Basic Usage ### Working with Public Keys ```typescript import { encodePublicKey, decodePublicKey } from 'snstr'; // Convert a hex public key to npub format const npub = encodePublicKey('3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'); console.log(npub); // npub1... // Convert an npub back to hex format const pubkey = decodePublicKey(npub); console.log(pubkey); // 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d ``` ### Working with Private Keys ```typescript import { encodePrivateKey, decodePrivateKey } from 'snstr'; // Convert a hex private key to nsec format const nsec = encodePrivateKey('3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'); console.log(nsec); // nsec1... // Convert an nsec back to hex format const privkey = decodePrivateKey(nsec); console.log(privkey); // 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d ``` ### Working with Note IDs ```typescript import { encodeNoteId, decodeNoteId } from 'snstr'; // Convert a hex note ID to note format const note = encodeNoteId('3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'); console.log(note); // note1... // Convert a note back to hex format const noteId = decodeNoteId(note); console.log(noteId); // 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d ``` ### Working with Complex Entities (TLV) #### Profiles (nprofile) ```typescript import { encodeProfile, decodeProfile, filterProfile } from 'snstr'; // Create a profile with relays const profileData = { pubkey: '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d', relays: ['wss://relay.example.com', 'wss://relay2.example.com'] }; // Encode the profile data const nprofile = encodeProfile(profileData); console.log(nprofile); // nprofile1... // Decode the nprofile back to profile data const decodedProfile = decodeProfile(nprofile); console.log(decodedProfile); // { // pubkey: '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d', // relays: ['wss://relay.example.com', 'wss://relay2.example.com'] // } // ⚠️ Security Note: Filter invalid relay URLs after decoding const safeProfile = filterProfile(decodedProfile); ``` #### Events (nevent) ```typescript import { encodeEvent, decodeEvent, filterEvent } from 'snstr'; // Create an event reference with relays and author const eventData = { id: '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d', relays: ['wss://relay.example.com'], author: 'pubkey_here', kind: 1 }; // Encode the event data const nevent = encodeEvent(eventData); console.log(nevent); // nevent1... // Decode the nevent back to event data const decodedEvent = decodeEvent(nevent); // Apply security filtering const safeEvent = filterEvent(decodedEvent); ``` #### Addresses (naddr) ```typescript import { encodeAddress, decodeAddress, filterAddress } from 'snstr'; // Create an address for a parameterized replaceable event const addressData = { identifier: 'profile', pubkey: '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d', kind: 30023, relays: ['wss://relay.example.com'] }; // Encode the address data const naddr = encodeAddress(addressData); console.log(naddr); // naddr1... // Decode the naddr back to address data const decodedAddress = decodeAddress(naddr); // Apply security filtering const safeAddress = filterAddress(decodedAddress); ``` ### Universal Decoder ```typescript import { decode, filterEntity } from 'snstr'; // Decode any NIP-19 entity const result = decode('npub1xxxxxx...'); console.log(result); // { // type: 'npub', // data: '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d' // } // For complex types, apply security filtering const complexResult = decode('nprofile1...'); if (['nprofile', 'nevent', 'naddr'].includes(complexResult.type)) { const safeData = filterEntity(complexResult.data); // Use safeData... } ``` ## Implementation Details - Uses the `@scure/base` library for Bech32 encoding and decoding - Implements TLV (Type-Length-Value) encoding for complex entities - Includes robust validation for relay URLs and other entity components - Handles large bech32 strings with custom limits - Optimized error handling with detailed error messages ## Security Considerations When working with NIP-19 entities, keep these security considerations in mind: ### Decoding Security The NIP-19 specification requires decoders to be permissive, which means our decoder will include potentially invalid URLs in the output. Always filter decoded entities to prevent security risks. ### Security Features The SNSTR implementation includes several security enhancements: 1. **Size Constraints**: - Maximum of 20 TLV entries (reduced from the typical 100) - Maximum relay URL length: 512 characters - Maximum identifier length: 1024 characters - Default bech32 data limit: 5000 bytes 2. **URL Validation During Encoding**: - Enforces `wss://` or `ws://` protocol - Rejects URLs with credentials (username/password) - Validates URL structure ### Security Utilities The library provides built-in functions to help filter potentially malicious data: ```typescript import { isValidRelayUrl, // Check a single URL filterProfile, // Filter nprofile entities filterEvent, // Filter nevent entities filterAddress, // Filter naddr entities filterEntity // Generic filter for any entity type } from 'snstr'; // Step 1: Decode the entity const profile = decodeProfile(nprofileString); // Step 2: Filter invalid/malicious relay URLs const safeProfile = filterProfile(profile); // Check individual URLs if (isValidRelayUrl('wss://relay.example.com')) { // URL is safe to use } ``` ### Safe Decoding Pattern Always follow this pattern when working with complex NIP-19 entities: ```typescript import { decode, filterEntity } from 'snstr'; function safelyDecodeEntity(bech32Str) { try { const result = decode(bech32Str); // Simple types don't need filtering if (['npub', 'nsec', 'note'].includes(result.type)) { return result.data; } // Complex types need filtering if (['nprofile', 'nevent', 'naddr'].includes(result.type)) { return filterEntity(result.data); } return null; } catch (error) { console.error('Error decoding entity:', error); return null; } } // Usage const safeEntity = safelyDecodeEntity(bech32String); ``` For a complete example, see [nip19-security.ts](../../examples/nip19/nip19-security.ts). ## TypeScript Types The SNSTR implementation provides complete TypeScript typings with zero `any` types for all NIP-19 entities. Here's a breakdown of the typing system: ### Basic Types ```typescript // Hexadecimal string (32-byte public key, private key, or note ID) type HexString = string; // Bech32 encoded string (prefix1data format) type Bech32String = `${string}1${string}`; // Valid relay URL string (ws:// or wss://) type RelayUrl = string; ``` ### Entity Data Types ```typescript // Profile data structure (nprofile) interface ProfileData { pubkey: HexString; relays?: RelayUrl[]; } // Event data structure (nevent) interface EventData { id: HexString; relays?: RelayUrl[]; author?: HexString; kind?: number; } // Address data structure (naddr) interface AddressData { identifier: string; pubkey: HexString; kind: number; relays?: RelayUrl[]; } ``` ### Decoded Entity Types ```typescript // Union type for all possible decoded entity types type DecodedEntity = | { type: "npub"; data: HexString } | { type: "nsec"; data: HexString } | { type: "note"; data: HexString } | { type: "nprofile"; data: ProfileData } | { type: "nevent"; data: EventData } | { type: "naddr"; data: AddressData }; ``` ### TLV Types ```typescript // Type-Length-Value entry structure interface TLVEntry { type: TLVType | number; value: Uint8Array; } // TLV types enum enum TLVType { Special = 0, // Depends on prefix: pubkey for nprofile, event id for nevent, identifier (d tag) for naddr Relay = 1, // Optional relay URL where the entity might be found Author = 2, // Author pubkey (for naddr, required; for nevent, optional) Kind = 3, // Event kind (for naddr, required; for nevent, optional) } ``` ### Safety with Type Validation The typing system ensures that: 1. All entity structures are properly validated 2. Encoding/decoding functions have precise input and output types 3. Security functions like `filterProfile`, `filterEvent`, and `filterAddress` preserve types 4. The universal `decode` function returns a discriminated union of all possible entity types These types make it easier to work with NIP-19 entities in TypeScript by providing autocompletion and type checking.

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AustinKelsay/nostr-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server