The Problem
Nostr clients use two fundamentally different relay strategies:
Looks up each user's declared relays via kind:10002 events
Users: Bob, Diana, Frank
Only uses a fixed list of relays configured by the user
Users: Alice, Carol, Eve
When some clients use outbox and others don't, network fragmentation occurs.
Scenario 1: Legacy Users on Same Big Relays
What happens:
Alice and Carol both use legacy clients configured with the same popular relays. They can see each other because their relay sets happen to overlap.
✓ Communication works (by luck of relay overlap)
Scenario 2: Outbox User Posts to Personal Relay
What happens:
Bob uses the outbox model correctly and posts only to his declared outbox relays (his personal relay). Alice's legacy client only checks the big public relays she configured.
✗ Alice cannot see Bob's posts - she doesn't know he exists
Scenario 3: Reply Discovery Failure
What happens:
Diana replies to Carol's post. Diana's outbox client correctly sends the reply to Carol's inbox relays. But Alice's legacy client doesn't know to check Carol's inbox.
✗ Alice sees Carol's post but Diana's reply is invisible to her
Scenario 4: Mention Delivery Failure
What happens:
Eve mentions Bob in a post. Eve's legacy client doesn't look up Bob's inbox relays - it just posts to Eve's configured relays. Bob's outbox client checks his inbox relays for mentions, but Eve's post isn't there.
✗ Bob never sees Eve's mention of him
Scenario 5: The Centralization Trap
What happens:
To ensure visibility with legacy clients, users are forced to blast their posts to many big relays. This defeats Nostr's decentralization goal and creates censorship chokepoints.
⚠ Works but centralizes around ~5 mega-relays - censorship vulnerability
Silent Failures: "The Dog That Doesn't Bark"
The most dangerous aspect: users have no idea they're missing content.
👻 Missing Follows
Alice follows Bob. Her legacy client adds Bob's pubkey to her follow list but doesn't know where Bob posts. She thinks she's following him, but her feed shows nothing from Bob.
💬 Phantom Conversations
Carol sees a post with "47 replies" but her client only shows 12. The other 35 replies were sent to inbox relays by outbox clients. Carol thinks the conversation is quiet.
⭐ Lost Zaps & Reactions
Frank zaps Eve's post, sending it to Eve's inbox relay. Eve's legacy client doesn't check there. She never sees the zap or the sats.
🔗 Broken Quote Posts
Alice sees a quote-post referencing a note. She clicks it but gets "Note not found" - the original was on the author's personal outbox relay her client doesn't know about.
Summary of Problems
🔇 Silent Failures
Users don't know they're missing content. No errors, just empty feeds and incomplete threads.
💬 Broken Threads
Replies sent to inbox relays are invisible to legacy clients. Conversations appear fragmented.
👤 Profile Invisibility
Following someone doesn't mean you'll see their posts if they use different relays.
🏢 Forced Centralization
Users must post to big relays to be seen, creating censorship chokepoints.
📩 Lost Mentions
Mentions and DMs fail when clients don't deliver to recipient's inbox relays.
💸 Missing Zaps
Reactions and zaps sent to inbox relays never reach users on legacy clients.
The Hard Part: Assembling Your Feed
Finding replies is relatively easy. Building a feed from your follows is where outbox gets hard.
🟢 Easy: Finding Replies
- You have the event ID you're looking for
- Events often include relay hints in tags
- Check a few popular relays + hints
- One event = bounded search
🔴 Hard: Building Your Feed
- You follow 300 people
- Need to find WHERE each person posts
- Fetch kind:10002 for all 300 people
- Each has 2-5 outbox relays
- Connect to hundreds of unique relays
- Subscribe, merge, deduplicate
📊 The Connection Explosion
Legacy Mode
5-10
Fixed relay connections
Outbox Mode
follows × relays_per_user
300 × 3 = 900
Potential connections
Even with deduplication (many users share relays), you might still need 200-400 unique relay connections.
Case Study: Amethyst's "Boiling the Ocean"
A real-world example of outbox complexity from one of Nostr's most popular Android clients.
"Amethyst in my experience tries to boil the ocean so much that it really has a hard time in general... not due to outbox making notifs hard, but because it connects to 900 relays at startup so it really just shows whatever it can before the network stack dies."
— Nostr user feedback
What Happens at Startup:
User follows 300 accounts
Query kind:10002 for each followed pubkey
300 users × 3 relays = 900 potential connections
Try to connect to all relays simultaneously
Too many concurrent connections, handshakes time out, app becomes unresponsive
The Global Network Reality
Outbox model assumes reliable, fast internet. Much of the world doesn't have that.
🌐 Developed Region
- 50-1000 Mbps fiber/5G
- Latency: 10-30ms
- Unlimited data plans
- Stable connections
✓ Outbox works well
🌍 Developing Region
- 2G/3G: 0.1-2 Mbps
- Latency: 200-500ms
- Expensive, metered data
- Frequent disconnections
✗ Outbox is painful
🔌 Connection Overhead
Each WebSocket connection requires a TCP handshake + TLS negotiation. With 200+ relays, that's minutes of setup time on slow networks.
📱 Mobile Limits
Mobile OSes limit concurrent connections. Android/iOS may kill apps that open too many sockets.
🔋 Battery Drain
Maintaining hundreds of WebSocket connections keeps the radio active, draining battery rapidly.
💰 Data Costs
Connection setup overhead alone can consume megabytes. In countries where data costs $5-10/GB, this matters.
Developer Implementation Challenges
Outbox isn't just "query different relays." It's a fundamental architectural change.
Legacy Client Code
// Simple: fixed relay list
const relays = ['wss://relay.damus.io', 'wss://nos.lol'];
const events = await pool.querySync(relays, filter);
Complexity: Low
Outbox Client Code
// Complex: dynamic relay discovery
for (const pubkey of followList) {
const relayList = await fetchKind10002(pubkey);
const outboxRelays = parseOutboxRelays(relayList);
relayMap.set(pubkey, outboxRelays);
}
const uniqueRelays = deduplicateRelays(relayMap);
const connectionPool = await managedConnect(uniqueRelays, {
maxConcurrent: 50,
timeout: 5000,
retryStrategy: exponentialBackoff
});
const events = await mergeAndDeduplicate(
await Promise.all(subscriptions)
);
Complexity: High
What Developers Must Implement:
- Relay list discovery — Where to find kind:10002 events? Bootstrap relays? Cache them?
- Connection pooling — Can't open 900 connections at once. Need queuing, limits, priorities.
- Rate limiting — Each relay has different limits. Track and respect them.
- Graceful degradation — What if a relay is down? Slow? Returns errors?
- Caching strategies — Cache relay lists? For how long? Invalidation?
- Subscription management — Hundreds of subscriptions across hundreds of relays.
- Result merging — Same event from multiple relays. Deduplicate efficiently.
When Legacy Mode is Acceptable
Not every Nostr application needs full outbox support. Here's when simpler is better.
🤖 Bots & Automated Posters
A bot posting weather updates or price feeds doesn't need to discover user relays. Post to 3-5 popular relays, done.
🔔 Simple Notification Services
A service that posts alerts to a known audience can use fixed relays that the audience monitors.
📚 Learning & Prototyping
Building your first Nostr app? Start with legacy mode. Add outbox later when you understand the protocol.
🔧 Resource-Constrained Environments
IoT devices, embedded systems, or apps for low-end phones may not handle connection complexity.
🎯 Niche Communities
A closed community that agrees to use specific relays doesn't need outbox discovery.
⚡ High-Performance Scrapers
Indexing services may prefer to connect to major relays only, accepting incomplete data for speed.
Outbox Model: Honest Assessment
✅ Advantages
- True decentralization — No single relay is a chokepoint
- Censorship resistance — Can't silence users by blocking one relay
- User sovereignty — You control where your data lives
- Reliable delivery — Replies/mentions reach recipients' preferred relays
- Resilience — Network works even if big relays go down
- Spam reduction — Personal relays can have strict policies
❌ Disadvantages
- Connection explosion — Hundreds of simultaneous connections
- Implementation complexity — 10x more code than legacy
- Poor network hostility — Unusable on slow/metered connections
- Battery/resource intensive — Many connections = high power use
- Cold start problem — Need kind:10002 before you can find anyone
- Debugging difficulty — "Why can't I see this post?" has many answers
- Bootstrap complexity — Where do you find relay lists initially?
The Path Forward: Intermediate Solutions
The ecosystem needs libraries and infrastructure that abstract away outbox complexity.
📚 Outbox Libraries
High-level libraries that handle relay discovery, connection pooling, and caching internally. Developers call getPostsFromFollows() without worrying about the plumbing.
Status: Emerging (nostr-tools, NDK improving)
🔄 Relay Aggregators
Services that do the outbox work server-side. Your client connects to one endpoint that fans out to many relays. Trade-off: adds a trust point.
Status: Some exist (relay proxies)
💾 Relay List Caches
Dedicated services that index kind:10002 events so clients don't need to search multiple relays for relay lists.
Status: purplepag.es, some indexers
📱 Adaptive Strategies
Clients that detect network conditions and fall back to simpler modes. Fast WiFi? Full outbox. Slow mobile? Top 20 relays only.
Status: Needed, rarely implemented
Final Verdict
Outbox is the right architectural goal for Nostr's decentralization promise. But it's not a simple switch to flip.
For End Users
Use clients that support outbox (Damus, Amethyst, etc.) but understand that on slow networks, experience may suffer. Publish your kind:10002 relay list.
For Client Developers
Implement outbox, but progressively. Start with relay list discovery, add connection pooling, implement graceful degradation. Use existing libraries when possible.
For Bot/Tool Developers
It's OK to use legacy mode for simple use cases. Focus on posting to well-known relays. Add outbox for reading if you need comprehensive data.
For the Ecosystem
We need better libraries, better infrastructure, and better documentation. The outbox model is correct but currently too hard to implement well.
The fragmentation problem is real. But so are the implementation challenges. The answer isn't "everyone must do outbox now" — it's "let's build the tools that make outbox accessible to everyone."