NDK Core Expert Agent - Comprehensive Codebase Analysis
- NDK Core - Expert Agent Knowledge Base
- Overview
- Executive Summary
- Public API Surface
- Core Modules Breakdown
- 1. NDK Orchestration (src/ndk/ - 46KB)
- 2. Event Model (src/events/ - 38KB + 37 kinds)
- 3. Relay Management (src/relay/)
- 4. Subscriptions (src/subscription/)
- 5. Signing Strategies (src/signers/)
- 6. User Management (src/user/)
- 7. Caching Layer (src/cache/)
- 8. AI Guardrails (src/ai-guardrails/ - 33 Checks)
- Architecture Patterns
- Key Types & Constants
- Testing & Development
- Documentation Available
- Known Antipatterns & Safeguards
- Integration Highlights
- Critical Design Principles
- File Organization
- Key Statistics
NDK Core - Expert Agent Knowledge Base
Overview
This is a comprehensive analysis of the Nostr Development Kit (NDK) Core package, designed to train an expert agent on the architecture, design patterns, and implementation details of the library.
Project: NDK v3.0.0
Root: /Users/customer/Work/NDK-nhlteu/core
Analysis Depth: Very Thorough
Executive Summary
NDK is a comprehensive TypeScript/JavaScript library for building Nostr applications. It provides:
- Event Model: Flexible
NDKEventclass for constructing, signing, encrypting, and publishing Nostr events - Relay Management: Intelligent relay pool handling with connection pooling, auth, and flapping detection
- Subscriptions: Stream-based event fetching with caching strategies and filter optimization
- User System: Profile management, follow lists, and user discovery
- Multiple Signers: NIP-07 (browser ext), NIP-46 (remote), private-key strategies
- Extensible Cache: Pluggable cache adapters with module system
- AI Guardrails: 33 runtime checks catching 90% of common LLM/dev mistakes
- 37+ Event Kinds: Specialized handlers for articles, tasks, zaps, nutzaps, etc.
Public API Surface
Main Classes (15+)
| Class | Purpose | Key Methods |
|---|---|---|
NDK |
Main orchestration | subscribe(), fetchEvents(), publish(), getUser(), count() |
NDKEvent |
Event model | sign(), publish(), encrypt(), decrypt(), tag() |
NDKSubscription |
Event streams | .on("event"), .on("eose"), .stop() |
NDKRelay |
Single relay | connect(), disconnect(), subscribe(), publish() |
NDKPool |
Multi-relay mgmt | addRelay(), useTemporaryRelay(), connection lifecycle |
NDKRelaySet |
Filtered relays | Targeted publish(), subscribe(), count() |
NDKUser |
User/profile | pubkey, npub, profile, getZapInfo(), follows() |
NDKSigner |
Signing interface | sign(), encrypt(), decrypt(), serialization |
AIGuardrails |
Runtime validation | 33 checks, granular control |
Constructor Configuration
interface NDKConstructorParams {
explicitRelayUrls?: string[]; // Direct relay connections
outboxRelayUrls?: string[]; // Gossip model relays
enableOutboxModel?: boolean = true;
autoConnectUserRelays?: boolean = true;
signer?: NDKSigner;
cacheAdapter?: NDKCacheAdapter;
aiGuardrails?: boolean | { skip: Set<string> };
clientName?: string;
filterValidationMode?: "validate" | "fix" | "ignore";
signatureVerificationWorker?: Worker;
futureTimestampGrace?: number;
}
Core Modules Breakdown
1. NDK Orchestration (src/ndk/ - 46KB)
Responsibilities:
- Lifecycle management (connect/disconnect)
- Signer & cache integration
- Subscription coordination
- Event publishing orchestration
- AI Guardrails enforcement
- Request queueing
Key Files:
index.ts- NDK class (main)queue/- Request throttling systemactive-user.ts- Active user trackingfetch-event-from-tag.ts- Event resolution
2. Event Model (src/events/ - 38KB + 37 kinds)
Responsibilities:
- Event construction and mutation
- Signature signing/verification
- Content encryption (NIP-04, NIP-44)
- Automatic content tagging (mentions, hashtags)
- Event serialization
- 37+ specialized event kind handlers
Key Files:
index.ts- NDKEvent classvalidation.ts- Signature verificationencryption.ts- NIP-04/44 encryptioncontent-tagger.ts- Auto-taggingkinds/- Article, Task, Zap, Highlight, Nutzap, etc.
Example Event Kinds:
Kind 0: Metadata (Profiles)
Kind 1: Text Notes
Kind 3: Contacts (Follow lists)
Kind 30023: Articles (NIP-23)
Kind 30000-30063: NIP-51 Lists (Mutes, Pins, etc.)
Kind 9735: Zaps (NIP-57)
Kind 30061: Nutzap (NIP-61)
3. Relay Management (src/relay/)
Responsibilities:
- WebSocket lifecycle management
- Multi-relay connection pooling
- NIP-42 relay authentication
- NIP-11 relay info fetching
- Keep-alive & reconnection backoff
- Signature verification stats
- Flapping detection & scoring
Key Files:
index.ts- NDKRelay (single connection)pool/index.ts- NDKPool (multi-relay)sets/index.ts- NDKRelaySet (filtered groups)connectivity.ts- WebSocket lifecyclesubscription.ts- Per-relay subscriptionsauth-policies.ts- NIP-42 handling
Relay Status Machine:
DISCONNECTED → CONNECTING → CONNECTED → AUTH_REQUESTED → AUTHENTICATED
↓
RECONNECTING/FLAPPING
4. Subscriptions (src/subscription/)
Responsibilities:
- Stream-based event fetching
- Filter grouping & optimization
- Cache integration (4 modes)
- EOSE handling
- Relay set calculation
- Event deduplication
Cache Modes:
ONLY_CACHE // Skip relays, cache only
CACHE_FIRST // Cache first, relay fallback
PARALLEL // Query cache AND relays
ONLY_RELAY // Skip cache, relays only
Filter Specification (NIP-01):
{
ids?: string[];
kinds?: number[];
authors?: string[];
since?: number;
until?: number;
limit?: number;
search?: string;
['#p']?: string[]; // Mentions
['#e']?: string[]; // Replies
['#t']?: string[]; // Tags
['#a']?: string[]; // NIP-33 addresses
}
5. Signing Strategies (src/signers/)
Responsibilities:
- Abstract signing interface
- Multiple signing implementations
- Encryption/decryption support
- Relay preference discovery
- Signer serialization/persistence
Strategies:
- NIP-07 - Browser extensions (MetaMask, Alby, Nos2x)
- NIP-46 - Remote signing via relay (bunker/nsecBunker)
- Private Key - Direct signing (fastest, least safe)
Interface:
interface NDKSigner {
get pubkey(): string;
blockUntilReady(): Promise<NDKUser>;
sign(event: NostrEvent): Promise<string>;
relays?(ndk?: NDK): Promise<NDKRelay[]>;
encrypt(recipient: NDKUser, value: string): Promise<string>;
decrypt(sender: NDKUser, value: string): Promise<string>;
toPayload(): string; // Serialization
}
6. User Management (src/user/)
Responsibilities:
- User identity (pubkey/npub/nprofile)
- Profile metadata (NIP-01 kind 0)
- Follow list retrieval
- Zap endpoint discovery
- NIP-05 identity verification
Key Data:
class NDKUser {
pubkey: string; // Hex public key
npub: string; // Bech32
nprofile: string; // With relay hints
profile?: NDKUserProfile; // Metadata cache
relayUrls: string[]; // User preferences
nip46Urls: string[]; // Remote signing
}
7. Caching Layer (src/cache/)
Responsibilities:
- Define cache adapter interface
- Support cache extensions (modules)
- Filter ephemeral kinds
- Schema migration framework
Adapter Interface:
interface NDKCacheAdapter {
query(filters: NDKFilter[]): Promise<NDKEvent[]>;
saveEvent(event: NDKEvent): Promise<void>;
warmupCache(filters?: NDKFilter[]): Promise<void>;
}
Known Implementations:
- @nostr-dev-kit/ndk-cache-dexie (Browser)
- @nostr-dev-kit/ndk-cache-sqlite (Node/Bun)
- @nostr-dev-kit/ndk-cache-redis (Server)
8. AI Guardrails (src/ai-guardrails/ - 33 Checks)
33 Runtime Checks catch common mistakes:
Filter Validation (7):
- Bech32 in filter arrays (npub instead of hex)
- Invalid hex values
- Large limits (> 1000)
- Empty filters
- Timestamp logic (since > until)
- Invalid NIP-33 tags
Event Construction (8):
- Missing kind
- Param-replaceable without d-tag
- created_at in milliseconds (not seconds)
- Content as object (not string)
- Modified after signing
Subscriptions (4):
- Not started (forgot
.start()) - closeOnEose without handler
- Wrong argument type
Plus: Tags, Relay, Validation checks
Configuration:
// Enable globally
const ndk = new NDK({ aiGuardrails: true });
// Enable with exceptions
const ndk = new NDK({ aiGuardrails: { skip: new Set(['check-id']) } });
// Disable for one call
ndk.guardrailOff().fetchEvents(filter);
ndk.guardrailOff('specific-check').subscribe(filter);
Architecture Patterns
Flow Diagrams
Orchestration Stack:
NDK Instance
├─ NDKPool (Relay connections)
│ ├─ NDKRelay (WebSocket per relay)
│ └─ NDKRelaySet (Filtered groups)
├─ NDKSubscriptionManager (Active subscriptions)
├─ NDKSigner (Sign/encrypt)
├─ NDKCacheAdapter (Persistence)
└─ AIGuardrails (Validation)
Subscription Lifecycle:
ndk.subscribe(filter, options)
├─ Calculate relay set
├─ Group with similar filters
├─ Create NDKSubscription
└─ Start (cache query + relay REQ)
├─ Emit cached events (onEvents)
├─ Emit relay events (onEvent)
├─ Emit EOSE (onEose)
└─ Emit close (onClose)
Event Publishing:
event.sign(signer)
event.publish(relaySet?)
├─ Calculate relay set if needed
├─ Connect relays
├─ Send EVENT message
├─ Handle NIP-42 auth if needed
└─ Track per-relay results
├─ Emit "published"
└─ Emit "publish:failed"
Extension Points
- Custom Signers - Implement
NDKSigner - Cache Backends - Implement
NDKCacheAdapter - Cache Modules - Define
CacheModuleDefinition - Event Kinds - Subclass
NDKEvent - Relay Auth - Custom
NDKAuthPolicyfunctions - Event Filters - Relay list resolution (NIP-65)
Key Types & Constants
Event Construction
type NDKRawEvent = {
created_at: number;
content: string;
tags: string[][];
kind: NDKKind | number;
pubkey: string;
id: string;
sig: string;
};
Filter Validation Modes
"validate" // Throw on undefined values
"fix" // Auto-remove undefined
"ignore" // Pass through (legacy)
Relay Status Enum
DISCONNECTED = 1
RECONNECTING = 2
FLAPPING = 3
CONNECTING = 4
CONNECTED = 5
AUTH_REQUESTED = 6
AUTHENTICATING = 7
AUTHENTICATED = 8
Encryption Schemes
"nip04" // Legacy (25519 + chacha20poly1305)
"nip44" // Modern (25519 + chacha20poly1305 with v2)
Testing & Development
Test Utilities Exported
// Mocks
export { RelayMock } from "./mocks/relay-mock";
export { RelayPoolMock } from "./mocks/relay-pool-mock";
// Generators
export { EventGenerator } from "./mocks/event-generator";
export { SignerGenerator } from "./helpers/test-fixtures";
export { UserGenerator } from "./helpers/test-fixtures";
// Helpers
export { TestFixture } from "./helpers/test-fixtures";
export { TimeController, withTimeControl } from "./helpers/time";
// Mocking data
export { mockNutzap, mockProof } from "./mocks/nutzaps";
Example Test Pattern
const relay = new RelayMock('wss://relay.example.com', {
connectionDelay: 100,
autoConnect: true,
});
const event = await EventGenerator.createSignedTextNote('Hello!', alice.pubkey);
relay.simulateEvent(event);
relay.simulateEOSE();
Documentation Available
Snippets (Code Examples):
- Event creation, signing, publishing
- User profile fetching
- Key generation (NIP-49)
- Subscription patterns
- Testing strategies
Tutorials:
- Relay authentication (NIP-42)
- Filter validation best practices
- Content muting/filtering
- NIP-19 encoding
- Signer persistence
- Performance optimization
- Zap integration
References:
- CHANGELOG.md - Historical changes
- MIGRATION-2.16.md - Breaking changes
- OUTBOX.md - Gossip model details
Known Antipatterns & Safeguards
Common Mistakes (Guarded)
- Bech32 in Filters - Use hex, not npub/note
- Timestamp Units - Seconds, not milliseconds
- Missing Kind - Always set event.kind
- No d-tag on Param-Replaceable - NIP-33 requires d-tag
- Unmanaged Subscriptions - Explicitly .stop()
- No Cache Adapter - Warns if not configured after 2.5s
- Large Limits - Limits > 1000 flagged
Performance Antipatterns (Warned)
- Overuse of
fetchEvents()- Use subscriptions instead - Empty filters - Add constraints (authors, kinds, etc.)
- No relay set calculation - Specify relays explicitly
Integration Highlights
Outbox Model
- Auto-queries purplepag.es & nos.lol
- Enabled by default
- Gossip-based relay discovery
Extensible Cache
- Modular cache system
- Custom collections & indexes
- Migration framework for schema evolution
37+ Event Kinds
- Text notes, articles, tasks
- Zaps, nutzaps, highlights
- Lists, metadata, profiles
- DVMs, classifications, wikis
Relay Authentication
- NIP-42 implementation
- Customizable auth policies
- Per-relay or global defaults
Critical Design Principles
- Async-First - Most operations return promises
- Event-Driven - EventEmitter for lifecycle hooks
- Streaming Subscriptions - Events as they arrive, not batches
- Flexible Caching - Multiple strategies per subscription
- Zero-Config Defaults - Works with minimal setup
- Developer Safety - 33 guardrails catch 90% of mistakes
- Performance-Optimized - Pooling, grouping, verification workers
- Highly Extensible - Custom signers, cache, kinds, auth
File Organization
src/
├── ndk/ # Orchestration (46KB)
├── events/ # Event model (38KB + 37 kinds)
├── relay/ # Relay management
├── subscription/ # Event streams
├── signers/ # NIP-07, NIP-46, Private-key
├── user/ # Profile & identity
├── cache/ # Cache interfaces
├── ai-guardrails/ # 33 runtime checks
├── outbox/ # Gossip model
├── zapper/ # NIP-57/61 utilities
└── utils/ # Helpers
test/
├── mocks/ # RelayMock, EventGenerator
├── helpers/ # Fixtures, utilities
└── index.ts # Test exports
Key Statistics
- Version: 3.0.0
- Main Classes: 15+
- Public Methods: 100+
- Event Kinds: 37+
- Guardrail Checks: 33
- Signer Strategies: 3
- Cache Modes: 4
- Test Utilities: 7+
- Documentation: Snippets, tutorials, migration guides
This analysis serves as the foundation for expert agent training on NDK Core architecture and implementation.
Write a comment