# Nostr Protocol for Agent-to-Agent Communication - Research Report

## 1. How Nostr Relays Work for Private/Encrypted Messaging

### Protocol Overview
- Nostr is a decentralized protocol based on WebSocket relays
- Clients connect to relays, publish signed events, and subscribe to event streams
- No accounts, no API keys, no registration - just secp256k1 keypairs
- Events are JSON objects with: id, pubkey, created_at, kind, tags, content, sig

### NIP-04 (Legacy Encrypted DMs - Kind 4)
- Uses shared secret via ECDH (secp256k1 Diffie-Hellman)
- Content encrypted with AES-256-CBC
- Format: `<encrypted_base64>?iv=<iv_base64>`
- P-tag reveals recipient pubkey (metadata leak)
- Widely supported by all relays and clients
- GOOD ENOUGH for agent communication (agents don't need metadata privacy)

### NIP-44 (Modern Encrypted DMs)
- Uses XChaCha20-Poly1305 with HKDF key derivation
- Better padding, authenticated encryption
- Used with NIP-17 (kind 1059 gift-wrapped DMs) for metadata privacy
- Recommended for new implementations

### Relay Behavior for DMs
- Relays store kind:4 events and serve them to subscribers
- Filter by pubkey (p-tag) to get DMs addressed to you
- Most relays keep events indefinitely (or until storage limits)
- No relay authentication needed for basic usage

## 2. Python Libraries for Nostr

### nostr-sdk (RECOMMENDED)
- `pip install nostr-sdk` (v0.44.2)
- Rust bindings via UniFFI - very fast, full-featured
- Built-in: NIP-04, NIP-44, relay client, event builder, filters
- Async support, WebSocket transport included
- 3.4MB wheel, no compilation needed

### pynostr
- `pip install pynostr` (v0.7.0)
- Pure Python, lightweight
- NIP-04 encrypted DMs via EncryptedDirectMessage class
- RelayManager for WebSocket connections
- Good for simple use cases, more manual

### nostr (python-nostr)
- `pip install nostr` (v0.0.2)
- Very minimal, older
- Basic key generation only
- NOT recommended for production

## 3. Keypair Generation & Encrypted DMs

### Using nostr-sdk (recommended):
```python
from nostr_sdk import Keys, nip04_encrypt, nip04_decrypt, nip44_encrypt, nip44_decrypt, Nip44Version

# Generate keypair
keys = Keys.generate()
print(keys.public_key().to_bech32())  # npub1...
print(keys.secret_key().to_bech32())  # nsec1...

# NIP-04 encrypt/decrypt
encrypted = nip04_encrypt(sender_sk, recipient_pk, "message")
decrypted = nip04_decrypt(recipient_sk, sender_pk, encrypted)

# NIP-44 encrypt/decrypt (recommended)
encrypted = nip44_encrypt(sender_sk, recipient_pk, "message", Nip44Version.V2)
decrypted = nip44_decrypt(recipient_sk, sender_pk, encrypted)
```

### Using pynostr:
```python
from pynostr.key import PrivateKey

key = PrivateKey()  # Generate
encrypted = key.encrypt_message("hello", recipient_pubkey_hex)
decrypted = recipient_key.decrypt_message(encrypted, sender_pubkey_hex)
```

## 4. Minimum Viable Setup (TESTED & WORKING)

### Full working code (nostr-sdk):
```python
import asyncio
from datetime import timedelta
from nostr_sdk import (
    Keys, ClientBuilder, EventBuilder, Filter, Kind,
    nip04_encrypt, nip04_decrypt, Tag, NostrSigner, RelayUrl
)

RELAYS = ["wss://relay.damus.io", "wss://nos.lol"]

async def main():
    # Generate 3 agent keys
    timmy = Keys.generate()
    ezra = Keys.generate()
    bezalel = Keys.generate()

    # Connect Timmy to relays
    client = ClientBuilder().signer(NostrSigner.keys(timmy)).build()
    for r in RELAYS:
        await client.add_relay(RelayUrl.parse(r))
    await client.connect()
    await asyncio.sleep(3)

    # Send encrypted DM: Timmy -> Ezra
    msg = "Build complete. Deploy approved."
    encrypted = nip04_encrypt(timmy.secret_key(), ezra.public_key(), msg)
    builder = EventBuilder(Kind(4), encrypted).tags([
        Tag.public_key(ezra.public_key())
    ])
    output = await client.send_event_builder(builder)
    print(f"Sent to {len(output.success)} relays")

    # Fetch as Ezra
    ezra_client = ClientBuilder().signer(NostrSigner.keys(ezra)).build()
    for r in RELAYS:
        await ezra_client.add_relay(RelayUrl.parse(r))
    await ezra_client.connect()
    await asyncio.sleep(3)

    dm_filter = Filter().kind(Kind(4)).pubkey(ezra.public_key()).limit(10)
    events = await ezra_client.fetch_events(dm_filter, timedelta(seconds=10))
    for event in events.to_vec():
        decrypted = nip04_decrypt(ezra.secret_key(), event.author(), event.content())
        print(f"Received: {decrypted}")

asyncio.run(main())
```

### TESTED RESULTS:
- 3 keypairs generated successfully
- Message sent to 2 public relays (relay.damus.io, nos.lol)
- Message fetched and decrypted by recipient
- NIP-04 and NIP-44 both verified working
- Total time: ~10 seconds including relay connections

## 5. Recommended Public Relays

| Relay | URL | Notes |
|-------|-----|-------|
| Damus | wss://relay.damus.io | Popular, reliable |
| nos.lol | wss://nos.lol | Fast, good uptime |
| Nostr.band | wss://relay.nostr.band | Good for search |
| Nostr Wine | wss://relay.nostr.wine | Paid, very reliable |
| Purplepag.es | wss://purplepag.es | Good for discovery |

## 6. Can Nostr Replace Telegram for Agent Dispatch?

### YES - with caveats:

**Advantages over Telegram:**
- No API key or bot token needed
- No account registration
- No rate limits from a central service
- End-to-end encrypted (Telegram bot API is NOT e2e encrypted)
- Decentralized - no single point of failure
- Free, no terms of service to violate
- Agents only need a keypair (32 bytes)
- Messages persist on relays (no need to be online simultaneously)

**Challenges:**
- No push notifications (must poll or maintain WebSocket)
- No guaranteed delivery (relay might be down)
- Relay selection matters for reliability (use 2-3 relays)
- No built-in message ordering guarantee
- Slightly more latency than Telegram (~1-3s relay propagation)
- No rich media (files, buttons) - text only for DMs

**For Agent Dispatch Specifically:**
- EXCELLENT for: status updates, task dispatch, coordination
- Messages are JSON-friendly (put structured data in content)
- Can use custom event kinds for different message types
- Subscription model lets agents listen for real-time events
- Perfect for fire-and-forget status messages

**Recommended Architecture:**
1. Each agent has a persistent keypair (stored in config)
2. All agents connect to 2-3 public relays
3. Dispatch = encrypted DM with JSON payload
4. Status updates = encrypted DMs back to coordinator
5. Use NIP-04 for simplicity, NIP-44 for better security
6. Maintain WebSocket connection for real-time, with polling fallback

### Verdict: Nostr is a STRONG candidate for replacing Telegram
- Zero infrastructure needed
- More secure (e2e encrypted vs Telegram bot API)
- No API key management
- Works without any server we control
- Only dependency: public relays (many free ones available)
