Nostr Outbox Model vs Legacy Relay Mode

How mixed client strategies cause network fragmentation

The Problem

Nostr clients use two fundamentally different relay strategies:

Outbox Model (NIP-65)

Looks up each user's declared relays via kind:10002 events

Users: Bob, Diana, Frank

Legacy Mode

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:

1
Load follow list

User follows 300 accounts

2
Fetch relay lists

Query kind:10002 for each followed pubkey

3
Calculate unique relays

300 users × 3 relays = 900 potential connections

4
Open WebSocket connections

Try to connect to all relays simultaneously

💥
Network stack overwhelmed

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."