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:
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?;