web-app/docs/01-architecture/relay-hub.md
padreug cdf099e45f Create comprehensive Obsidian-style documentation structure
- Reorganize all markdown documentation into structured docs/ folder
- Create 7 main documentation categories (00-overview through 06-deployment)
- Add comprehensive index files for each category with cross-linking
- Implement Obsidian-compatible [[link]] syntax throughout
- Move legacy/deprecated documentation to archive folder
- Establish documentation standards and maintenance guidelines
- Provide complete coverage of modular architecture, services, and deployment
- Enable better navigation and discoverability for developers and contributors

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-06 14:31:27 +02:00

9 KiB

Central Relay Hub Architecture

Overview

The Ario application uses a centralized relay hub architecture to manage all Nostr relay connections. This design provides a single point of connection management, efficient resource sharing, and consistent relay behavior across all components.

Architecture Components

1. Core Relay Hub (relayHub.ts)

The RelayHub class is the foundation of the relay management system, built on top of nostr-tools SimplePool.

Key Features:

  • Singleton Pattern: Single instance manages all relay connections
  • Connection Pooling: Efficiently manages multiple relay connections
  • Automatic Reconnection: Handles connection failures and reconnection logic
  • Health Monitoring: Regular health checks and status reporting
  • Mobile Optimization: Handles mobile app visibility changes
  • Event Emission: Provides event-driven architecture for status updates

Location: src/modules/base/nostr/relay-hub.ts (moved from legacy lib/nostr location)

2. Relay Hub Composable (useRelayHub.ts)

A Vue 3 composable that provides a reactive interface to the relay hub.

Key Features:

  • Reactive State: isConnected, connectionStatus, relayStatuses
  • Connection Management: initialize(), connect(), disconnect()
  • Subscription Management: subscribe(), unsubscribe()
  • Event Publishing: publishEvent(), queryEvents()
  • Health Monitoring: Connection health percentages and status

Location: src/composables/useRelayHub.ts

3. Application Integration (App.vue)

The main application component initializes the relay hub at startup.

Key Integration Points:

// Initialize relay hub
const relayHub = useRelayHub()

onMounted(async () => {
  // Initialize relay hub
  try {
    await relayHub.initialize()
    console.log('Relay hub initialized successfully')
  } catch (error) {
    console.error('Failed to initialize relay hub:', error)
  }
})

How Components Plug Into the Relay Hub

1. Direct Integration via useRelayHub()

Components can directly use the relay hub composable:

import { useRelayHub } from '@/composables/useRelayHub'

export default {
  setup() {
    const relayHub = useRelayHub()
    
    // Access reactive state
    const isConnected = relayHub.isConnected
    const connectionStatus = relayHub.connectionStatus
    
    // Use connection methods
    const connect = async () => {
      await relayHub.connect()
    }
    
    // Subscribe to events
    const unsubscribe = relayHub.subscribe({
      id: 'my-subscription',
      filters: [{ kinds: [1] }], // Text notes
      onEvent: (event) => {
        console.log('Received event:', event)
      }
    })
    
    // Clean up on unmount
    onUnmounted(() => {
      unsubscribe()
    })
    
    return {
      isConnected,
      connectionStatus,
      connect
    }
  }
}

2. Indirect Integration via Specialized Composables

Components can use specialized composables that internally use the relay hub:

Chat Integration (useNostrChat.ts)

The chat system integrates with the relay hub for message handling:

// Inside useNostrChat.ts
const subscribeToPeer = async (peerPubkey: string) => {
  // Check connection status
  if (!relayHub.isConnected.value) {
    await relayHub.connect()
  }
  
  // Subscribe to peer messages
  const unsubscribe = relayHub.subscribe({
    id: `peer-${peerPubkey}`,
    filters: [
      {
        kinds: [4], // Direct messages
        '#p': [currentUser.value.pubkey],
        authors: [peerPubkey]
      }
    ],
    onEvent: (event) => handleIncomingMessage(event, peerPubkey)
  })
  
  return unsubscribe
}

Market Integration (useMarket.ts)

The market system can integrate similarly:

// Example market integration
const subscribeToMarketEvents = async () => {
  if (!relayHub.isConnected.value) {
    await relayHub.connect()
  }
  
  return relayHub.subscribe({
    id: 'market-events',
    filters: [
      {
        kinds: [30000], // Custom market events
        '#t': ['market']
      }
    ],
    onEvent: (event) => handleMarketEvent(event)
  })
}

3. Configuration and Setup

Relay Configuration

Relays are configured in the application config:

// src/lib/config.ts
export const config = {
  nostr: {
    relays: [
      'wss://relay.ariege.io',
      'wss://lnbits.ariege.io/nostrrelay/benac'
    ]
  }
}

Initialization Order

  1. App.vue initializes the relay hub
  2. Components can use the relay hub immediately after initialization
  3. Specialized composables handle their own relay hub integration

Benefits of the Central Relay Hub Architecture

1. Resource Efficiency

  • Single connection pool for all relay connections
  • Shared connection management across components
  • Reduced memory usage and connection overhead

2. Consistent Behavior

  • Unified connection status across the application
  • Consistent error handling and reconnection logic
  • Standardized relay health monitoring

3. Scalability

  • Easy to add new relay endpoints
  • Centralized configuration management
  • Simple to extend with new relay features

4. Maintainability

  • Single point of truth for relay logic
  • Easier debugging and monitoring
  • Centralized relay-related bug fixes

Best Practices for Component Integration

1. Always Check Connection Status

const myFunction = async () => {
  if (!relayHub.isConnected.value) {
    await relayHub.connect()
  }
  // Proceed with relay operations
}

2. Use Subscription IDs

const subscriptionId = `my-component-${Date.now()}`
const unsubscribe = relayHub.subscribe({
  id: subscriptionId,
  filters: [...],
  onEvent: handleEvent
})

3. Clean Up Subscriptions

onUnmounted(() => {
  if (unsubscribe) {
    unsubscribe()
  }
})

4. Handle Connection Errors

const connect = async () => {
  try {
    await relayHub.connect()
  } catch (error) {
    console.error('Failed to connect:', error)
    // Handle error appropriately
  }
}

5. Monitor Connection Health

const connectionHealth = computed(() => relayHub.connectionHealth)
const isHealthy = computed(() => connectionHealth.value > 50)

Example: Creating a New Relay-Enabled Component

Here's a complete example of a new component that uses the relay hub:

<template>
  <div>
    <div>Connection Status: {{ connectionStatus }}</div>
    <div>Connected Relays: {{ connectedRelayCount }}/{{ totalRelayCount }}</div>
    <button @click="subscribeToEvents" :disabled="!isConnected">
      Subscribe to Events
    </button>
    <div v-if="events.length">
      <h3>Received Events:</h3>
      <div v-for="event in events" :key="event.id">
        {{ event.content }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { useRelayHub } from '@/composables/useRelayHub'

const relayHub = useRelayHub()
const events = ref<any[]>([])
let unsubscribe: (() => void) | null = null

const { isConnected, connectionStatus, connectedRelayCount, totalRelayCount } = relayHub

const subscribeToEvents = async () => {
  if (!relayHub.isConnected.value) {
    await relayHub.connect()
  }
  
  unsubscribe = relayHub.subscribe({
    id: 'my-component-events',
    filters: [
      {
        kinds: [1], // Text notes
        limit: 10
      }
    ],
    onEvent: (event) => {
      events.value.push(event)
    }
  })
}

onUnmounted(() => {
  if (unsubscribe) {
    unsubscribe()
  }
})
</script>

Troubleshooting

Common Issues

  1. Connection Not Established

    • Check if relay hub is initialized in App.vue
    • Verify relay URLs in configuration
    • Check browser console for connection errors
  2. Subscriptions Not Working

    • Ensure connection is established before subscribing
    • Verify subscription filters are correct
    • Check if unsubscribe function is called prematurely
  3. Performance Issues

    • Monitor number of active subscriptions
    • Use appropriate filter limits
    • Clean up unused subscriptions

Debug Tools

The relay hub provides several debugging capabilities:

// Check connection status
console.log('Connected:', relayHub.isConnected.value)
console.log('Relay count:', relayHub.totalRelayCount)

// Monitor relay health
console.log('Health:', relayHub.connectionHealth.value)

// Check specific relay status
const status = relayHub.getRelayStatus('wss://relay.example.com')
console.log('Relay status:', status)

Conclusion

The central relay hub architecture provides a robust, efficient, and maintainable way to manage Nostr relay connections across the Ario application. By following the integration patterns outlined in this document, components can easily leverage the relay hub's capabilities while maintaining clean separation of concerns and optimal performance.

For questions or issues with relay hub integration, refer to the existing implementations in useNostrChat.ts and useMarket.ts as reference examples.