Nostr WoT

Documentation

Tout ce dont vous avez besoin pour intégrer le Web of Trust dans votre application.

API Oracle

Version 0.3.0 : distance orientée entre abonnements et observations publiques distinctes de masquage via HTTP. Aucune extension requise.

Serveur public et format des requêtes

URL de base : https://wot-oracle.mappingbitcoin.com. L’Oracle ne nécessite aucune clé API.

Envoyez les clés publiques sous forme de chaînes hexadécimales complètes de 64 caractères minuscules, et non de npub. Les exemples utilisent des clés publiques fictives pour illustrer la structure des réponses ; les résultats de votre graphe seront différents. Les requêtes POST utilisent Content-Type: application/json.

Le point d’accès racine GET / indique la version du service, la documentation et les points d’accès disponibles. Pour votre propre instance, consultez le guide d’auto-hébergement.

Les distances utilisent les liens d’abonnement orientés de type 3. Les listes publiques de masquage de type 10000 sont des observations distinctes. L’Oracle ne les combine pas en un score de confiance et ne retire pas les comptes masqués des chemins d’abonnement.

Points d’accès

GET/health

État de fonctionnement du processus et version publiée.

json
{
  "status": "healthy",
  "version": "0.3.0"
}
terminal
$curl "https://wot-oracle.mappingbitcoin.com/health"

Un processus sain peut encore attendre des événements des relais ou ne pas pouvoir enregistrer les mises à jour. Utilisez /ready pour vérifier si l’ingestion est prête.

GET/ready

État de l’ingestion : HTTP 200 si elle est prête, HTTP 503 sinon.

L’état prêt exige une ingestion en cours, aucune défaillance actuelle de la base de données et la réception d’un événement d’abonnement ou de masquage depuis moins de cinq minutes. Un relais privé peu actif peut donc produire un code 503 même si le processus fonctionne. Exemple en attendant le premier événement :

json
{
  "running": true,
  "ready": false,
  "last_event_received_at": 0,
  "last_persisted_at": 0,
  "persisted_events": 0,
  "lagged_notifications": 0,
  "persistence_errors": 0,
  "coverage": "configured_relays_only"
}

Les horodatages sont en secondes Unix ; zéro signifie qu’aucune observation n’a encore eu lieu. persisted_events compte les mises à jour d’auteurs acceptées et enregistrées après regroupement des lots. lagged_notifications et persistence_errors signalent les problèmes d’ingestion.

GET/stats

Statistiques du graphe indexé, paramètres du cache, métriques de verrouillage et état de l’ingestion.

json
{
  "node_count": 3,
  "edge_count": 2,
  "nodes_with_follows": 2,
  "mute_edge_count": 0,
  "nodes_with_mute_lists": 1,
  "sync": {
    "running": true,
    "ready": false,
    "last_event_received_at": 0,
    "last_persisted_at": 0,
    "persisted_events": 0,
    "lagged_notifications": 0,
    "persistence_errors": 0,
    "coverage": "configured_relays_only"
  },
  "cache": {
    "size": 0,
    "capacity": 100000,
    "ttl_secs": 300
  },
  "locks": {
    "write_lock_count": 0,
    "write_lock_avg_us": 0,
    "write_lock_max_us": 0,
    "read_lock_count": 0,
    "read_lock_avg_us": 0,
    "read_lock_max_us": 0
  }
}

Les nombres et paramètres ci-dessus sont illustratifs. edge_count compte les liens d’abonnement ; mute_edge_count compte les liens publics de masquage de clés publiques. nodes_with_mute_lists inclut les listes connues sans entrée publique de clé publique. Les durées de verrouillage sont en microsecondes. L’objet sync a les mêmes champs que /ready.

GET/distance

Plus courte distance d’abonnement orientée entre deux clés publiques.

  • from et to : clés publiques source et cible obligatoires.
  • max_hops : de 1 à 5, 3 par défaut.
  • include_bridges : booléen, false par défaut. Inclut les nœuds de rencontre de la recherche lorsqu’ils sont disponibles.
  • bypass_cache : booléen, false par défaut. Recalcule à partir du graphe actuellement indexé ; ne récupère pas de nouvelles données auprès des relais.
json
{
  "from": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "to": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
  "hops": 2,
  "path_count": 1,
  "mutual_follow": false
}

hops vaut zéro pour la distance à soi-même et un pour un abonnement direct. Une valeur null signifie qu’aucun chemin n’a été trouvé dans la profondeur demandée du graphe indexé. Cela ne prouve pas qu’aucune connexion n’existe ailleurs sur Nostr.

path_count compte les plus courts chemins orientés, même lorsque les ponts sont omis ; le compteur sature à la valeur maximale d’un entier non signé de 64 bits. mutual_follow indique un abonnement direct dans les deux sens. Le champ facultatif bridges contient les nœuds de rencontre de la recherche, et non un chemin complet ni une preuve de chemins disjoints.

terminal
$curl "https://wot-oracle.mappingbitcoin.com/distance?from=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&to=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&max_hops=3"

POST/distance/batch

Interroger jusqu’à 100 cibles depuis une source, en conservant l’ordre des cibles et les doublons.

Champs JSON obligatoires : from et targets. Les champs facultatifs max_hops, include_bridges et bypass_cache utilisent les mêmes valeurs par défaut que /distance.

Requête

json
{
  "from": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "targets": [
    "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
  ],
  "max_hops": 3
}

Réponse

json
{
  "from": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "results": [
    {
      "from": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "to": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
      "hops": 2,
      "path_count": 1,
      "mutual_follow": false
    }
  ]
}

Chaque résultat contient from et to.

GET/path

Renvoyer les clés publiques intermédiaires d’un des plus courts chemins d’abonnement orientés.

from et to obligatoires ; max_hops facultatif (de 1 à 5, 3 par défaut).

json
{
  "from": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "to": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
  "path": [
    "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
  ]
}

L’exemple représente deux liens d’abonnement, de la source au pont puis à la cible. La source et la cible sont exclues de path. Les chemins vers soi-même et les abonnements directs renvoient un tableau vide ; l’absence de chemin dans la profondeur demandée renvoie null. Cette réponse ne contient pas de champ hops.

GET/follows

Paginer la liste d’abonnements actuellement indexée d’une clé publique.

pubkey obligatoire ; offset (0 par défaut) et limit (500 par défaut, plafond de 5000) facultatifs. total indique la taille complète de la liste indexée, indépendamment de la taille de page.

json
{
  "pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "follows": [
    "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
  ],
  "total": 1
}

Une clé publique inconnue renvoie une liste vide et total: 0.

GET/common-follows

Renvoyer les clés publiques auxquelles les deux comptes sont directement abonnés.

Paramètres obligatoires : from et to.

json
{
  "from": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "to": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
  "common_follows": [
    "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
  ]
}
terminal
$curl "https://wot-oracle.mappingbitcoin.com/common-follows?from=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&to=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"

GET/mutes

Paginer les entrées publiques de clés publiques d’une liste de masquage de type 10000 indexée.

pubkey obligatoire ; offset (0 par défaut) et limit (500 par défaut, plafond de 5000) facultatifs. total indique la taille complète de la liste indexée, indépendamment de la taille de page.

json
{
  "pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "mutes": [],
  "total": 0,
  "public_list_known": true
}

public_list_known: false signifie qu’aucun événement de liste de masquage n’a été indexé pour cette clé publique. Un événement connu peut ne contenir aucune entrée publique de clé publique, comme ci-dessus. Les entrées chiffrées sont inaccessibles à l’Oracle ; une liste publique connue et vide peut donc contenir des entrées chiffrées. Les balises de masquage de mots, de hashtags et de fils sont exclues des observations relatives aux clés publiques.

GET/trust

Renvoyer la distance d’abonnement avec des observations publiques distinctes de masquage.

  • from et to : clés publiques source et cible obligatoires.
  • max_hops : de 1 à 5, 3 par défaut.
  • include_bridges : booléen, false par défaut. Inclut les nœuds de rencontre de la recherche lorsqu’ils sont disponibles.
  • bypass_cache : booléen, false par défaut. Recalcule à partir du graphe actuellement indexé ; ne récupère pas de nouvelles données auprès des relais.
json
{
  "follow_distance": {
    "from": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "to": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
    "hops": 2,
    "path_count": 1,
    "mutual_follow": false
  },
  "public_mute_evidence": {
    "source_mutes_target": false,
    "target_mutes_source": false,
    "followed_muters": [
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
    ],
    "source_mute_list_known": true,
    "target_mute_list_known": false
  }
}

followed_muters répertorie les comptes auxquels la source est directement abonnée et dont les listes publiques de masquage indexées contiennent la cible. Les deux booléens de masquage direct décrivent la relation entre source et cible ; les indicateurs de liste connue distinguent les listes absentes des listes publiques connues.

Les masquages peuvent exprimer une préférence personnelle. L’absence d’observation publique ne constitue pas une recommandation. Les clients décident de l’utilisation de ces observations : la réponse n’applique aucun poids, score agrégé ou exclusion automatique. La distance d’abonnement et les observations de masquage peuvent être lues à des instants légèrement différents pendant l’ingestion.

Couverture et fraîcheur des données

sync.coverage vaut configured_relays_only. Les résultats décrivent les événements indexés depuis les relais configurés sur le serveur, sans garantie de couverture globale ou complète. Les entrées du cache sont invalidées lorsque la révision du graphe change. Ni l’état prêt ni le contournement du cache ne garantissent que les relais ont renvoyé le dernier événement.

La mise en cache des profils de type 0, /profiles et include_profiles ne sont pas implémentés dans la version 0.3.0.

Limites et erreurs

Les points d’accès aux données utilisent un seau à jetons par adresse IP configuré par RATE_LIMIT_PER_MINUTE. Les limites dépendent du déploiement ; les requêtes peuvent être rejetées après une rafale même avant qu’une minute ne se soit écoulée. Les routes racine, /health et /ready sont exemptées de cette limitation. Les corps des requêtes sont limités à 1 Mio. Ne supposez pas que chaque réponse contient des en-têtes de limitation de débit.

Les erreurs de validation et de calcul de l’application renvoient du JSON avec error et code :

json
{
  "error": "Invalid pubkey format",
  "code": "INVALID_PUBKEY"
}
  • 400 : INVALID_PUBKEY, INVALID_MAX_HOPS ou TOO_MANY_TARGETS.
  • 413 : le corps de la requête dépasse la limite de taille.
  • 429 : limite de débit par IP dépassée. Attendez avant de réessayer ; respectez le délai indiqué s’il est fourni.
  • 500 : INTERNAL_ERROR.
  • 503 : QUERY_BUSY lorsque la capacité de traitement est épuisée, ou un état de préparation avec ready: false provenant de /ready.

Les paramètres de requête mal formés, le JSON mal formé et les rejets par les intergiciels peuvent utiliser un autre format de corps. Vérifiez le statut HTTP avant d’analyser une réponse comme réussie. Utilisez un nombre limité de tentatives avec des délais croissants en cas de surcharge temporaire.