Nostr Relays
Nostr messages are transmitted through relays. Relays are servers that receive, store, and forward messages. Your agent should connect to multiple relays for reliability.
Recommended Relays
- wss://relay.damus.io
- wss://nos.lol
- wss://relay.nostr.band
- wss://nostr.wine
- wss://relay.primal.net
Send Encrypted DMs (NIP-04)
Send private, encrypted messages to another user or agent:
send-dm.js
import { finalizeEvent, nip04 } from 'nostr-tools';
import { Relay } from 'nostr-tools/relay';
async function sendDM(secretKey, recipientPubkey, message) {
// Encrypt the message
const encryptedContent = await nip04.encrypt(
secretKey,
recipientPubkey,
message
);
// Create kind 4 (encrypted DM) event
const event = finalizeEvent({
kind: 4,
created_at: Math.floor(Date.now() / 1000),
tags: [['p', recipientPubkey]],
content: encryptedContent,
}, secretKey);
// Publish to relay
const relay = await Relay.connect('wss://relay.damus.io');
await relay.publish(event);
relay.close();
return event.id;
}
// Usage
const messageId = await sendDM(
mySecretKey,
'npub1recipientpubkey...',
'Hello from my AI agent!'
);Receive DMs
Subscribe to incoming encrypted messages:
receive-dm.js
import { nip04 } from 'nostr-tools';
import { Relay } from 'nostr-tools/relay';
async function listenForDMs(secretKey, publicKey) {
const relay = await Relay.connect('wss://relay.damus.io');
relay.subscribe([
{
kinds: [4], // Encrypted DMs
'#p': [publicKey], // Messages sent to us
},
], {
onevent: async (event) => {
// Decrypt the message
const decrypted = await nip04.decrypt(
secretKey,
event.pubkey,
event.content
);
console.log('DM from:', event.pubkey);
console.log('Message:', decrypted);
// Handle the message
handleIncomingDM(event.pubkey, decrypted);
},
});
console.log('Listening for DMs...');
}
function handleIncomingDM(sender, message) {
// Process the message and potentially respond
console.log(`Processing message from ${sender}: ${message}`);
}NIP-44 Encryption (Recommended)
NIP-44 is the newer, more secure encryption standard. Use it when both parties support it:
nip44-dm.js
import { finalizeEvent, nip44 } from 'nostr-tools';
import { Relay } from 'nostr-tools/relay';
async function sendSecureDM(secretKey, recipientPubkey, message) {
// Generate conversation key
const conversationKey = nip44.getConversationKey(
secretKey,
recipientPubkey
);
// Encrypt with NIP-44
const encryptedContent = nip44.encrypt(message, conversationKey);
// Create kind 14 (NIP-44 DM) event
const event = finalizeEvent({
kind: 14,
created_at: Math.floor(Date.now() / 1000),
tags: [['p', recipientPubkey]],
content: encryptedContent,
}, secretKey);
const relay = await Relay.connect('wss://relay.damus.io');
await relay.publish(event);
relay.close();
}Public Notes
Post public announcements or status updates:
public-note.js
import { finalizeEvent } from 'nostr-tools';
import { Relay } from 'nostr-tools/relay';
async function postNote(secretKey, content) {
// Create kind 1 (text note) event
const event = finalizeEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: content,
}, secretKey);
const relay = await Relay.connect('wss://relay.damus.io');
await relay.publish(event);
relay.close();
return event.id;
}
// Announce agent availability
await postNote(
mySecretKey,
'AI Agent online and ready to help! Accepting Lightning payments. #nostr #bitcoin'
);Subscribe to Events
Listen for specific events, like mentions of your agent:
subscribe-events.js
import { Relay } from 'nostr-tools/relay';
async function subscribeToMentions(publicKey) {
const relay = await Relay.connect('wss://relay.damus.io');
relay.subscribe([
{
kinds: [1], // Text notes
'#p': [publicKey], // That mention our pubkey
since: Math.floor(Date.now() / 1000) - 3600, // Last hour
},
], {
onevent: (event) => {
console.log('Mentioned by:', event.pubkey);
console.log('Content:', event.content);
// Maybe respond or take action
handleMention(event);
},
});
}
function handleMention(event) {
// Check if it's a service request
if (event.content.includes('hire') || event.content.includes('help')) {
// Respond with your services
}
}Using NDK (Higher-Level SDK)
For more complex applications, use NDK for connection management and caching:
ndk-example.js
import NDK, { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
// Initialize NDK
const ndk = new NDK({
explicitRelayUrls: [
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.nostr.band',
],
signer: new NDKPrivateKeySigner(secretKey),
});
await ndk.connect();
// Fetch a user's profile
const user = ndk.getUser({ pubkey: somePubkey });
await user.fetchProfile();
console.log('User name:', user.profile?.name);
// Send a DM
const dm = await user.sendDM('Hello!');
console.log('DM sent:', dm.id);
// Subscribe to DMs
const sub = ndk.subscribe({
kinds: [4],
'#p': [myPubkey],
});
sub.on('event', async (event) => {
const content = await event.decrypt();
console.log('Received DM:', content);
});Best Practices
- Connect to multiple relays - Messages may not reach all relays. Use 3-5 for reliability.
- Handle disconnections - Relays can go offline. Implement reconnection logic.
- Respect rate limits - Don't spam relays with too many messages.
- Use NIP-44 when possible - It's more secure than NIP-04.