notecrumbs

a nostr opengraph server build on nostrdb and egui
git clone git://jb55.com/notecrumbs
Log | Files | Refs | README | LICENSE

commit fa31e572a269a68cd513b8821d3feeda82358c16
parent 9223e19f613481b41d38496c44153b2f0fe52adf
Author: alltheseas <alltheseas@users.noreply.github.com>
Date:   Wed, 22 Oct 2025 11:26:22 -0500

Document relay discovery sources

Diffstat:
MCargo.lock | 263+++----------------------------------------------------------------------------
MREADME.md | 5+++++
Msrc/html.rs | 4+---
Msrc/render.rs | 36++++++++++++++++--------------------
4 files changed, 32 insertions(+), 276 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -216,7 +216,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash", "shlex", "syn 1.0.109", "which", @@ -239,7 +239,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash", "shlex", "syn 2.0.107", "which", @@ -421,12 +421,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] name = "chacha20" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -550,7 +544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "typenum", ] @@ -1000,11 +994,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", "wasip2", - "wasm-bindgen", ] [[package]] @@ -1283,23 +1275,6 @@ dependencies = [ ] [[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http 1.3.1", - "hyper 1.7.0", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots 1.0.3", -] - -[[package]] name = "hyper-tls" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1508,16 +1483,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] - -[[package]] name = "itertools" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1648,12 +1613,6 @@ dependencies = [ ] [[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] name = "mac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1967,7 +1926,6 @@ dependencies = [ "nostr-sdk", "nostrdb", "pulldown-cmark", - "reqwest", "serde_json", "skia-safe", "tokio", @@ -2098,7 +2056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -2151,7 +2109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand 0.8.5", + "rand", ] [[package]] @@ -2310,61 +2268,6 @@ dependencies = [ ] [[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash 2.1.1", - "rustls", - "socket2 0.6.1", - "thiserror 2.0.17", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash 2.1.1", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.17", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.6.1", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] name = "quote" version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2386,18 +2289,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_chacha", + "rand_core", ] [[package]] @@ -2407,17 +2300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", + "rand_core", ] [[package]] @@ -2430,15 +2313,6 @@ dependencies = [ ] [[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] name = "raw-cpuid" version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2512,46 +2386,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] -name = "reqwest" -version = "0.12.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.7.0", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 1.0.3", -] - -[[package]] name = "resvg" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2604,12 +2438,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2665,7 +2493,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ - "web-time", "zeroize", ] @@ -2735,7 +2562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes 0.14.0", - "rand 0.8.5", + "rand", "secp256k1-sys", "serde", ] @@ -2823,18 +2650,6 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3052,15 +2867,6 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3347,45 +3153,6 @@ dependencies = [ ] [[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags 2.10.0", - "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] name = "tower-service" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3472,7 +3239,7 @@ dependencies = [ "http 1.3.1", "httparse", "log", - "rand 0.8.5", + "rand", "rustls", "rustls-pki-types", "sha1", @@ -3720,16 +3487,6 @@ dependencies = [ ] [[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] name = "web_atoms" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/README.md b/README.md @@ -26,3 +26,8 @@ Very alpha. The design is still a bit rough, but getting there: <img style="width: 600px; height: 300px" src="https://damus.io/nevent1qqstj0wgdgplzypp5fjlg5vdr9mcex5me7elhcvh2trk0836y69q9cgsn6gzr.png"> +## Relay discovery & metrics + +- Notecrumbs keeps long-lived relay connections and now learns new relays from every event it ingests. Relay list (`kind:10002`) events and contact lists (`kind:3`) are parsed for `r`/`relays` tags as well as per-contact relay hints, and the pool deduplicates and connects to any valid URLs it sees. +- Per-request fetch loops feed those hints straight into the shared pool, so visiting a profile helps warm future requests that need the same relays. +- Relay pool health counters (ensure calls, added relays, connect successes/failures, and active relay count) are exposed via Prometheus at `http://127.0.0.1:3000/metrics`, and a mirrored summary is logged every 60 seconds. diff --git a/src/html.rs b/src/html.rs @@ -1,9 +1,7 @@ use crate::Error; use crate::{ abbrev::{abbrev_str, abbreviate}, - render::{ - is_image_url, NoteAndProfileRenderData, ProfileRenderData, PROFILE_FEED_RECENT_LIMIT, - }, + render::{NoteAndProfileRenderData, ProfileRenderData, PROFILE_FEED_RECENT_LIMIT}, Notecrumbs, }; use ammonia::Builder as HtmlSanitizer; diff --git a/src/render.rs b/src/render.rs @@ -13,20 +13,18 @@ use nostr::types::{RelayUrl, SingleLetterTag, Timestamp}; use nostr_sdk::async_utility::futures_util::StreamExt; use nostr_sdk::nips::nip19::Nip19; use nostr_sdk::prelude::{Event, EventId, PublicKey}; +use nostr_sdk::JsonUtil; use nostrdb::{ Block, BlockType, Blocks, FilterElement, FilterField, Mention, Ndb, Note, NoteKey, ProfileKey, ProfileRecord, Transaction, }; -use std::sync::Arc; use std::collections::{BTreeMap, BTreeSet, HashSet}; +use std::sync::Arc; use std::time::SystemTime; use tokio::time::{timeout, Duration}; use tracing::{debug, error, warn}; const PURPLE: Color32 = Color32::from_rgb(0xcc, 0x43, 0xc5); -const MAX_IMAGE_BYTES: usize = 10 * 1024 * 1024; -const MAX_IMAGE_WIDTH: f32 = 900.0; -const MAX_IMAGE_HEIGHT: f32 = 260.0; pub const PROFILE_FEED_RECENT_LIMIT: usize = 12; pub const PROFILE_FEED_LOOKBACK_DAYS: u64 = 30; @@ -357,14 +355,14 @@ pub async fn fetch_profile_feed( ndb: Ndb, pubkey: [u8; 32], ) -> Result<()> { - use nostr_sdk::JsonUtil; - let relay_targets = collect_profile_relays(relay_pool.clone(), ndb.clone(), pubkey).await?; let relay_targets_arc = Arc::new(relay_targets); let cutoff = SystemTime::now() - .checked_sub(Duration::from_secs(60 * 60 * 24 * PROFILE_FEED_LOOKBACK_DAYS)) + .checked_sub(Duration::from_secs( + 60 * 60 * 24 * PROFILE_FEED_LOOKBACK_DAYS, + )) .and_then(|ts| ts.duration_since(SystemTime::UNIX_EPOCH).ok()) .map(|dur| dur.as_secs()); @@ -554,8 +552,6 @@ async fn collect_profile_relays( ndb: Ndb, pubkey: [u8; 32], ) -> Result<Vec<RelayUrl>> { - use nostr_sdk::JsonUtil; - relay_pool .ensure_relays(relay_pool.default_relays().iter().cloned()) .await?; @@ -624,19 +620,19 @@ async fn stream_profile_feed_once( pubkey: [u8; 32], since: Option<u64>, ) -> Result<usize> { - use nostr_sdk::JsonUtil; - - let author_ref = [&pubkey]; - let mut filter_builder = nostrdb::Filter::new() - .authors(author_ref) - .kinds([1]) - .limit(PROFILE_FEED_RECENT_LIMIT as u64); + let filter = { + let author_ref = [&pubkey]; + let mut builder = nostrdb::Filter::new() + .authors(author_ref) + .kinds([1]) + .limit(PROFILE_FEED_RECENT_LIMIT as u64); - if let Some(since) = since { - filter_builder = filter_builder.since(since); - } + if let Some(since) = since { + builder = builder.since(since); + } - let filter = convert_filter(&filter_builder.build()); + convert_filter(&builder.build()) + }; let mut stream = relay_pool .stream_events(vec![filter], &relays, Duration::from_millis(2000)) .await?;