4NK_node/archive/docs/explain_node_discovery.md
Nicolas Cantu b935cbab20 Réorganisation complète : tests, documentation et nettoyage
- Réorganisation des tests par catégorie (unit, integration, connectivity, external)
- Création de scripts d'exécution automatisés pour les tests
- Création de guides techniques complets (ARCHITECTURE.md, API.md)
- Transfert des informations depuis specs/ vers docs/
- Nettoyage et archivage des fichiers obsolètes
- Documentation complète des tests avec exemples
- Scripts de maintenance et nettoyage automatique
- Structure professionnelle prête pour l'évolution
2025-08-25 14:13:26 +02:00

9.3 KiB

Découverte des Nœuds et Ajout de Nœuds Externes

Comment les relais découvrent-ils initialement les autres nœuds ?

1. Découverte automatique (actuelle)

Actuellement, les relais utilisent une découverte statique basée sur les noms de conteneurs Docker :

// Dans sync.rs - discover_relays()
let relay_hosts = vec![
    "sdk_relay_1",
    "sdk_relay_2",
    "sdk_relay_3",
];

Mécanisme :

  1. Chaque relais a une liste prédéfinie des autres relais
  2. Au démarrage, il tente de se connecter à chaque relais de la liste
  3. Il ignore son propre nom (relay_id)
  4. Il ajoute les relais découverts à sa liste known_relays

2. Configuration par relay_id

Chaque relais a un relay_id unique dans sa configuration :

# .conf.docker.relay1
relay_id=relay-1

# .conf.docker.relay2
relay_id=relay-2

# .conf.docker.relay3
relay_id=relay-3

3. Partage de la liste des relais

Une fois connectés, les relais partagent leur liste de relais connus via des messages RelaySync :

// Création d'un message RelaySync
pub fn create_relay_sync(&self) -> Result<SyncMessage> {
    let known_relays = self.known_relays.lock().unwrap();
    let relays: Vec<RelayInfo> = known_relays.values().cloned().collect();

    let payload = SyncPayload::RelayData {
        relays,
        network_topology,
    };

    Ok(SyncMessage::new(SyncType::RelaySync, self.relay_id.clone(), payload))
}

Comment ajouter un nœud externe ?

Option 1: Modification de la liste statique

Pour ajouter un nœud externe, il faut modifier le code source :

// Dans sync.rs - discover_relays()
let relay_hosts = vec![
    "sdk_relay_1",
    "sdk_relay_2",
    "sdk_relay_3",
    "external-relay-1",  // ← Nouveau nœud externe
    "external-relay-2",  // ← Autre nœud externe
];

Avantages :

  • Simple à implémenter
  • Contrôle total sur les nœuds autorisés

Inconvénients :

  • Nécessite une recompilation
  • Pas flexible pour l'ajout dynamique
  • Centralisé

Option 2: Configuration externe (recommandée)

Créer un fichier de configuration pour les nœuds externes :

# external_nodes.conf
[relays]
external-relay-1 = "external-relay-1.example.com:8090"
external-relay-2 = "external-relay-2.example.com:8090"
external-relay-3 = "192.168.1.100:8090"

[discovery]
auto_discover = true
bootstrap_nodes = [
    "bootstrap-1.4nk.net:8090",
    "bootstrap-2.4nk.net:8090"
]

Modification du code pour supporter la configuration :

pub struct DiscoveryConfig {
    pub external_relays: HashMap<String, String>,
    pub auto_discover: bool,
    pub bootstrap_nodes: Vec<String>,
}

impl SyncManager {
    pub fn load_discovery_config(&self) -> Result<DiscoveryConfig> {
        // Charger depuis external_nodes.conf
    }

    pub async fn discover_relays(&self) -> Result<()> {
        let config = self.load_discovery_config()?;

        // Découverte locale (Docker)
        for host in &["sdk_relay_1", "sdk_relay_2", "sdk_relay_3"] {
            // ... logique existante
        }

        // Découverte externe
        for (relay_id, address) in &config.external_relays {
            let relay_info = RelayInfo {
                relay_id: relay_id.clone(),
                address: address.clone(),
                // ... autres champs
            };
            self.add_relay(relay_info)?;
        }

        // Découverte via bootstrap nodes
        if config.auto_discover {
            for bootstrap in &config.bootstrap_nodes {
                self.discover_via_bootstrap(bootstrap).await?;
            }
        }

        Ok(())
    }
}

Option 3: Découverte via DNS

Utiliser des enregistrements DNS pour la découverte :

pub async fn discover_via_dns(&self, domain: &str) -> Result<()> {
    // Résoudre les enregistrements SRV ou A
    let addresses = dns_lookup::get_host_addresses(domain)?;

    for addr in addresses {
        let relay_info = RelayInfo {
            relay_id: format!("relay-{}", addr),
            address: format!("{}:8090", addr),
            // ... autres champs
        };
        self.add_relay(relay_info)?;
    }

    Ok(())
}

Option 4: Découverte via API REST

Créer une API pour l'enregistrement des nœuds :

// Endpoint pour enregistrer un nouveau nœud
POST /api/relays/register
{
    "relay_id": "external-relay-1",
    "address": "external-relay-1.example.com:8090",
    "capabilities": ["sync", "mesh"],
    "version": "1.0.0"
}

// Endpoint pour récupérer la liste des nœuds
GET /api/relays/list

Implémentation recommandée

1. Créer un fichier de configuration externe

# Créer le fichier de configuration
cat > 4NK/4NK_node/sdk_relay/external_nodes.conf << EOF
[relays]
# Nœuds externes connus
external-relay-1 = "external-relay-1.example.com:8090"
external-relay-2 = "192.168.1.100:8090"

[discovery]
auto_discover = true
bootstrap_nodes = [
    "bootstrap-1.4nk.net:8090"
]

[security]
allowed_domains = [
    "*.4nk.net",
    "*.example.com"
]
EOF

2. Modifier le code pour supporter la configuration

// Dans sync.rs
use std::fs;
use toml;

#[derive(Debug, Deserialize)]
struct ExternalConfig {
    relays: HashMap<String, String>,
    discovery: DiscoverySettings,
    security: SecuritySettings,
}

#[derive(Debug, Deserialize)]
struct DiscoverySettings {
    auto_discover: bool,
    bootstrap_nodes: Vec<String>,
}

#[derive(Debug, Deserialize)]
struct SecuritySettings {
    allowed_domains: Vec<String>,
}

impl SyncManager {
    pub fn load_external_config(&self) -> Result<ExternalConfig> {
        let config_content = fs::read_to_string("external_nodes.conf")?;
        let config: ExternalConfig = toml::from_str(&config_content)?;
        Ok(config)
    }

    pub async fn discover_relays(&self) -> Result<()> {
        // Découverte locale (existante)
        let local_hosts = vec!["sdk_relay_1", "sdk_relay_2", "sdk_relay_3"];
        for host in local_hosts {
            // ... logique existante
        }

        // Découverte externe (nouvelle)
        if let Ok(config) = self.load_external_config() {
            for (relay_id, address) in &config.relays {
                let relay_info = RelayInfo {
                    relay_id: relay_id.clone(),
                    address: address.clone(),
                    sp_address: "".to_string(),
                    version: "1.0.0".to_string(),
                    uptime: 0,
                    last_seen: SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(),
                    capabilities: vec!["sync".to_string(), "mesh".to_string()],
                    health_status: HealthStatus::Healthy,
                };
                self.add_relay(relay_info)?;
            }
        }

        Ok(())
    }
}

3. Script pour ajouter un nœud externe

#!/bin/bash
# add_external_node.sh

RELAY_ID=$1
ADDRESS=$2
CONFIG_FILE="4NK/4NK_node/sdk_relay/external_nodes.conf"

if [ -z "$RELAY_ID" ] || [ -z "$ADDRESS" ]; then
    echo "Usage: $0 <relay_id> <address:port>"
    echo "Example: $0 external-relay-1 external-relay-1.example.com:8090"
    exit 1
fi

# Ajouter le nœud à la configuration
echo "Adding external node: $RELAY_ID -> $ADDRESS"

# Vérifier si le fichier existe
if [ ! -f "$CONFIG_FILE" ]; then
    cat > "$CONFIG_FILE" << EOF
[relays]

[discovery]
auto_discover = true
bootstrap_nodes = []

[security]
allowed_domains = ["*.4nk.net"]
EOF
fi

# Ajouter le nœud
sed -i "/^\[relays\]$/a $RELAY_ID = \"$ADDRESS\"" "$CONFIG_FILE"

echo "✅ External node added successfully!"
echo "🔄 Restart relays to apply changes:"
echo "   docker-compose restart sdk_relay_1 sdk_relay_2 sdk_relay_3"

Sécurité et validation

Validation des nœuds externes

impl SyncManager {
    pub fn validate_external_relay(&self, relay_info: &RelayInfo) -> Result<bool> {
        // Vérifier le format de l'adresse
        if !self.is_valid_address(&relay_info.address) {
            return Ok(false);
        }

        // Vérifier le domaine autorisé
        if !self.is_allowed_domain(&relay_info.address) {
            return Ok(false);
        }

        // Vérifier la connectivité
        if !self.can_connect_to_relay(&relay_info.address).await? {
            return Ok(false);
        }

        Ok(true)
    }

    pub async fn can_connect_to_relay(&self, address: &str) -> Result<bool> {
        // Test de connectivité WebSocket
        // ... logique de test
    }
}

Gestion des nœuds malveillants

impl SyncManager {
    pub fn blacklist_relay(&self, relay_id: &str, reason: &str) {
        // Ajouter à la liste noire
        // Arrêter les connexions
        // Notifier les autres relais
    }

    pub fn rate_limit_relay(&self, relay_id: &str) {
        // Limiter le nombre de messages
        // Surveiller les comportements suspects
    }
}

Conclusion

Recommandation : Utiliser l'Option 2 (Configuration externe) car elle offre :

  1. Flexibilité : Ajout de nœuds sans recompilation
  2. Sécurité : Contrôle des domaines autorisés
  3. Évolutivité : Support pour la découverte automatique
  4. Simplicité : Configuration par fichier

Prochaines étapes :

  1. Implémenter le support de external_nodes.conf
  2. Créer le script add_external_node.sh
  3. Ajouter la validation et la sécurité
  4. Tester avec des nœuds externes