Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nostr-did-resolver

Enterprise-grade W3C DID Resolution for the did:nostr method.

crates.io

Architecture

resolve("did:nostr:abc...#key1")
    │
    ▼
DID URL Parser ──► Cache Check ──► Resolution Chain
                                        │
                        ┌───────────────┼───────────────┐
                        ▼               ▼               ▼
                   RelayBackend    HttpBackend    OfflineBackend
                   (WebSocket)     (.well-known)   (pure math)
                   + circuit        + pooled
                     breaker          client
                        │               │               │
                        └───────────────┼───────────────┘
                                        ▼
                                 Build Result
                                        │
                                        ▼
                                 Dereference (if fragment)
                                        │
                                        ▼
                                 Cache (moka, bounded TTL)
                                        │
                                        ▼
                                 Metrics + Return

Features

  • Three-tier resolution chain — Relay (WebSocket) → HTTP (.well-known) → Offline (pure math). Always resolves.
  • Per-relay circuit breakers — Exponential backoff prevents hammering dead relays.
  • Event ID verification — Recomputation of SHA-256 canonical serialization before signature check catches relay tampering.
  • Schnorr signature verification — Every event cryptographically verified via BIP-340/secp256k1.
  • NIP-65 relay discovery — Kind 10002 events populate service endpoints.
  • NIP-39 identity claimsi tags populate alsoKnownAs for cross-network identity linking.
  • DID URL dereferencing#key1, #relay1 fragments return specific resources per W3C spec §7.2.
  • W3C-compliant metadatadidResolutionMetadata, didDocumentMetadata, content types, error codes.
  • Bounded TTL cache — moka-backed, LRU eviction, wait-free reads, no manual sweep.
  • Burst-absorbing rate limiter — Semaphore-based with configurable burst timeout.
  • Prometheus-style metrics — Lock-free atomic counters for every operational signal.
  • Builder validation — Zero values rejected at construction time, not at runtime.

Quick start

use nostr_did_resolver::Resolve;

#[tokio::main]
async fn main() {
    let resolver = Resolve::builder()
        .with_relay("wss://relay.damus.io")
        .with_relay("wss://nos.lol")
        .with_cache_ttl(300)
        .build()
        .unwrap();

    let result = resolver
        .resolve("did:nostr:124c0fa99407182ece5a24fad9b7f6674902fc422843d3128d38a0afbee0fdd2")
        .await;

    println!("{}", serde_json::to_string_pretty(&result).unwrap());
}

Resolution tiers

Tier Backend Network Failure mode Provides
1 Relay WebSocket Circuit breaker, retry, timeout Profile, follows, NIP-65 relays, NIP-39 identity claims
2 HTTP HTTPS GET Cross-verify key, pooled client, redirect limit Cached DID Document from .well-known
3 Offline None Never fails for valid DIDs Verification method only

Dereferencing

let result = resolver
    .dereference("did:nostr:124c0f...#key1")
    .await;

// Returns just the verificationMethod object per W3C spec §7.2

Offline resolution

let result = resolver.resolve_offline("did:nostr:124c0f...");
// Works without network — pure math from the public key

Metrics

let snapshot = resolver.metrics();
println!("Cache hit rate: {}/{}",
    snapshot.cache_hits,
    snapshot.resolutions_total
);
Metric Description
resolutions_total Total resolution attempts
cache_hits Served from cache
rate_limit_rejections Rejected by rate limiter
relay_successes Relay backend successes
relay_failures Relay backend failures
http_successes HTTP backend successes
http_failures HTTP backend failures
offline_resolutions Offline fallback resolutions
sig_verification_failures Events failing Schnorr verification
event_id_mismatches Events with tampered IDs
resolution_errors Total errors (invalidDid, notFound, etc.)

Configuration

let resolver = Resolve::builder()
    .with_relay("wss://relay.damus.io")      // Tier 1 relays
    .with_http_origin("https://example.com")  // Tier 2 HTTP cache
    .with_cache_ttl(300)                      // Cache TTL in seconds
    .with_cache_max_capacity(10_000)          // Max cache entries
    .with_relay_timeout(15)                   // Relay query timeout (sec)
    .with_http_timeout(10)                    // HTTP request timeout (sec)
    .with_max_concurrent(100)                 // Max concurrent resolutions
    .with_max_parallel_relays(4)              // Max parallel relay queries
    .with_max_relay_retries(2)                // Max retries per relay
    .with_burst_timeout_ms(100)               // Rate limiter burst window
    .build()
    .unwrap();

All numeric fields reject zero values — misconfiguration is caught at startup.

Dependencies

Crate Purpose
nostr-did DID Document types and builder
nostr-did-key BIP-340 → Multikey cryptographic transform
secp256k1 Schnorr signature verification
sha2 Event ID recomputation
tokio-tungstenite WebSocket relay connections
reqwest HTTP .well-known cache
moka Bounded TTL cache
uuid Subscription ID generation
tracing Instrumented logging
thiserror Error type derivation
static_assertions Compile-time Send + Sync verification

Safety

  • Event ID verified before signature — Cheap hash check catches relay tampering before expensive Schnorr verification.
  • Cross-verification of HTTP cachepublicKeyMultibase decoded and checked against the DID being resolved.
  • NIP-65 relay validation — Only wss:// and ws:// schemes accepted. HTTP relays rejected.
  • Follow pubkey validation — Kind 3 p tags validated as 64-char hex before inclusion.
  • Compile-time Send + Sync — Build breaks if any field is not thread-safe.
  • Zero values rejected — Builder returns error, not silent defaults.

License

MIT OR Apache-2.0

Related crates

About

Enterprise-grade DID resolution for did:nostr — three-tier chain (relay → HTTP → offline), circuit breakers, Schnorr verification, W3C-compliant metadata.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages