Skip to content
Draft
Show file tree
Hide file tree
Changes from 132 commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
6a06e9c
deps: bump pubky to relevant branch
ok300 Feb 15, 2026
c1c6c98
feat: split NexusWatcher into 3 parallel threads and exclude default …
Copilot Feb 15, 2026
efab342
Rename vars, methods to indicate they apply to external HSs
ok300 Feb 15, 2026
427e65d
deps: include latest updates from pubky
ok300 Feb 19, 2026
8dcdccf
Initial plan
Copilot Feb 19, 2026
3aa9a5d
fix: use to_z32() for PubkyId conversion to resolve pkarr version mis…
Copilot Feb 19, 2026
f00aba8
Merge pull request #71 from ok300/copilot/sub-pr-70
ok300 Feb 19, 2026
cb935d6
Fix PubkyId conversion from PublicKey
ok300 Feb 19, 2026
033aa4c
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Feb 19, 2026
fb9c064
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Feb 25, 2026
e681907
Bump pubky dependencies to latest 0.7.x
ok300 Feb 27, 2026
8c73b86
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Feb 27, 2026
51e113a
Fix merge conflicts
ok300 Feb 27, 2026
7b4a164
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Mar 2, 2026
0fc56ba
Bump pubky dependencies to latest 0.7.x
ok300 Mar 2, 2026
8e15bc5
Bump pubky dependencies to 0.7.0-rc2
ok300 Mar 2, 2026
f57d82c
refactor: extract periodic task loop into reusable `run_periodic_task…
ok300 Mar 6, 2026
6713068
test: Use unseeded testnets for nexus-watcher tests (#758)
ok300 Mar 9, 2026
a4d8756
`nexus-api` tests: use unseeded `Testnet` (#753)
ok300 Mar 9, 2026
878ce57
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Mar 10, 2026
cff0a52
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Mar 17, 2026
5ad570a
deps: bump pubky to latest release 0.7.0
ok300 Mar 18, 2026
0229c17
Fix flaky test, task runner scheduling
ok300 Mar 21, 2026
97a9a26
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Mar 23, 2026
49af0a0
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Mar 30, 2026
d1578a0
ref: runners and indexers (#783)
tipogi Apr 3, 2026
ccf5bb1
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Apr 3, 2026
265bc61
`TEventProcessor`: expand logic of `handle_error` (#788)
ok300 Apr 3, 2026
b318069
lint: cargo clippy
ok300 Apr 3, 2026
ec43bd7
feat: add `user-hs-resolver` task (#765)
ok300 Apr 3, 2026
fc72fb7
fix: empty external HS list should not result in error
ok300 Apr 3, 2026
e915f02
fix: ingest unknown users (#795)
tipogi Apr 6, 2026
e5efe4e
feat: first iteration of key based indexing
tipogi Apr 6, 2026
551eda3
chore: add TEventProcessor::instance_name() (#798)
ok300 Apr 6, 2026
c7546c3
feat: index per key based, delete batch processing. Improved error ha…
tipogi Apr 7, 2026
02a41d9
fix: skip invalid user public keys instead of aborting homeserver run
tipogi Apr 7, 2026
da32a2c
fix: skip bad events and advance cursor
tipogi Apr 8, 2026
73bf7a2
ref: extract shared event validation into Event::build
tipogi Apr 8, 2026
6bb307b
fix: remove stale HOSTED_BY edge if no HS is found for user (#791)
ok300 Apr 8, 2026
93ce106
On HS resolution, order PKs with bisection ordering (#797)
ok300 Apr 9, 2026
38108c5
chore: simplify Moderation init (#805)
ok300 Apr 9, 2026
a16b12f
chore: remove superfluous error helpers (#804)
ok300 Apr 9, 2026
49b6a4f
Merge branch 'origin-main' into feat/dx-events-by-user
ok300 Apr 11, 2026
04eb3d5
feat: record metrics for each run of the HS resolver task (#807)
ok300 Apr 17, 2026
763a54e
RetryEvent processor
aintnostressin Apr 9, 2026
ec6ba99
Merge branch 'feat/dx-events-by-user' into feat/retry-event
aintnostressin Apr 20, 2026
857f09c
retry_processor optional
aintnostressin Apr 20, 2026
2297419
start and end params in try_from_index_sorted_set
aintnostressin Apr 20, 2026
a63411f
fix infra errors handling
aintnostressin Apr 20, 2026
b079f9a
uncomment config
aintnostressin Apr 20, 2026
d63c12c
simplify fn
aintnostressin Apr 20, 2026
c945e8c
log cleanup
aintnostressin Apr 20, 2026
c7056c9
feat: add key_based_events_limit param in config file (#801)
tipogi Apr 21, 2026
ac5c0a8
fix ci
aintnostressin Apr 21, 2026
3955f48
Simplify homeserver process_event_lines(), re-use process_event_line(…
ok300 Apr 21, 2026
ce4074a
remove redundant config
aintnostressin Apr 21, 2026
82450c7
simplify config defaults
aintnostressin Apr 21, 2026
d5a87a3
simplify uri prefix stripping
aintnostressin Apr 21, 2026
c17d5de
uncomment config
aintnostressin Apr 21, 2026
914b79b
retry event key improvement
aintnostressin Apr 21, 2026
35c8c3e
fix: retry event index key
aintnostressin Apr 21, 2026
a1eac0a
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Apr 22, 2026
472542a
fix: remove duplicate watcher dependency
ok300 Apr 22, 2026
8b0dd2a
Merge branch 'feat/dx-events-by-user' into feat/retry-event
aintnostressin Apr 22, 2026
ce8958f
impl From trait
aintnostressin Apr 22, 2026
79d4676
remove unused fn
aintnostressin Apr 22, 2026
499baaa
add logging
aintnostressin Apr 22, 2026
a47afc2
use RetryEventIndexKey
aintnostressin Apr 22, 2026
db2ccad
Update nexus-watcher/src/service/indexer/mod.rs
aintnostressin Apr 22, 2026
677e09b
Update nexus-common/src/models/event/errors.rs
aintnostressin Apr 22, 2026
9b49217
revert back start & end params
aintnostressin Apr 23, 2026
56f8181
chore: move default impls to original positions
ok300 Apr 23, 2026
4a3c784
Move Universal Tags structs to separate module (#820)
ok300 Apr 23, 2026
cbfe4ad
Update nexus-watcher/src/events/retry/scheduler.rs
aintnostressin Apr 23, 2026
7510f32
Consolidate, simplify schedule_retry (#821)
ok300 Apr 23, 2026
bf9f205
Update nexus-watcher/src/events/retry/scheduler.rs
aintnostressin Apr 23, 2026
2c69041
Simplify moderation, revert unused TModeration trait (#822)
ok300 Apr 23, 2026
1de3399
chore: fix clippy warnings
ok300 Apr 23, 2026
1523f26
Update nexus-watcher/src/events/retry/processor.rs
aintnostressin Apr 23, 2026
786e143
Update nexus-watcher/src/events/retry/processor.rs
aintnostressin Apr 23, 2026
53d87ec
Update nexus-watcher/src/events/retry/processor.rs
aintnostressin Apr 23, 2026
d40bdd0
Update nexus-watcher/src/events/retry/store.rs
aintnostressin Apr 23, 2026
6b7fc63
fix test
aintnostressin Apr 23, 2026
92749ca
Flatten `PubkyClientError` variants (#823)
ok300 Apr 23, 2026
2258478
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 Apr 23, 2026
40927e7
chore: avoid vec allocation when building retry event index
ok300 Apr 23, 2026
7b3bdce
chore: fix clippy warnings
ok300 Apr 23, 2026
bda2422
simplify MockEventProcessor
aintnostressin Apr 23, 2026
e414fae
Update nexus-watcher/src/events/retry/processor.rs
aintnostressin Apr 24, 2026
78aea86
Batch RetryEvent::remove_from_index call (#826)
ok300 Apr 24, 2026
e2e0912
simplify error handling (#825)
aintnostressin Apr 24, 2026
92b965b
Batch RetryEvent::get_from_index (#827)
ok300 Apr 24, 2026
af1998f
Simplify RetryEvent::fetch_ready: return empty vec instead of None (#…
ok300 Apr 24, 2026
37e755b
Simplify RetryEvent::check_uri (#830)
ok300 Apr 24, 2026
7cd31ad
Simplify `EventHandler` by removing unused `moderation()` (#829)
ok300 Apr 24, 2026
eaf1e32
Remove unused universal_tag.rs (#831)
ok300 Apr 24, 2026
1fcc376
Simplify tag-path parsing, re-use `try_parse_app_tag_path` (#832)
ok300 Apr 24, 2026
44ba1d7
chore: fix clippy warning
ok300 Apr 24, 2026
b6426ce
chore: cargo fmt
ok300 Apr 24, 2026
70b0b69
remove RetryEventIndexKey
aintnostressin Apr 25, 2026
af8f2c7
add more test cases for multi event handling (#834)
aintnostressin Apr 27, 2026
433af82
add missing instrument macros
aintnostressin Apr 27, 2026
2b06d49
Merge branch 'feat/dx-events-by-user' into feat/user-key-indexing
aintnostressin Apr 27, 2026
9fe5048
feat: RetryEvent processor (#809)
aintnostressin Apr 27, 2026
4233fe3
Merge branch 'feat/retry-event' into feat/user-key-indexing
aintnostressin Apr 27, 2026
a84ae2f
chore: remove unnecessary Pubky parsing
ok300 Apr 27, 2026
d76f64b
Remove duplicate 404 check (#842)
ok300 Apr 27, 2026
b35efed
Merge remote-tracking branch 'origin/feat/dx-events-by-user' into fea…
aintnostressin Apr 28, 2026
e7567eb
chore: remove unused await (#844)
ok300 Apr 28, 2026
3e85d7e
chore: fix cargo clippy
ok300 Apr 28, 2026
09d1a8d
doc: fix field rustdoc
ok300 Apr 28, 2026
482dbdc
chore: extract EventType From impl
ok300 Apr 29, 2026
63e587b
fix: preserve WatcherConfig backward compatibility after new field (#…
ok300 Apr 29, 2026
e27060b
Simplify config field clamping, embed it in serde deserialization (#847)
ok300 Apr 29, 2026
711620f
refactor: consolidate event parsing (#848)
ok300 Apr 29, 2026
f060a9b
fix: validate user ID before handling event in key-based processor (#…
ok300 Apr 30, 2026
140ee66
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 May 4, 2026
8bbac14
doc: add more rustdocs to MockEventHandler
ok300 May 4, 2026
c3ddbd5
Merge remote-tracking branch 'origin/main' into feat/dx-events-by-user
ok300 May 8, 2026
488a7f1
Merge remote-tracking branch 'origin/feat/dx-events-by-user' into
ok300 May 8, 2026
761f8b3
Update stale is_retryable documentation
ok300 May 10, 2026
4c0e217
Refactor user_hs_cursor_key to avoid concat
ok300 May 10, 2026
902e0a8
Extract process_user_events method
ok300 May 10, 2026
609c16d
Log handling of unrecognized event URIs
ok300 May 10, 2026
82d91ad
chore: fix import
ok300 May 10, 2026
ee91d6f
Deserialization validators: add zero check
ok300 May 10, 2026
34866e9
Add missing `KeyBasedEventProcessor` tests (#859)
ok300 May 12, 2026
4e86c00
doc: add comments to clarify event stream use in fetch_events
ok300 May 13, 2026
64bfdd2
Simplify conversion from PublicKey to PubkyId
ok300 May 13, 2026
5c1cd72
Add TODO with possible future optimization
ok300 May 13, 2026
ff3c24c
fix: treat some PubkyClientError variants as infra errors
aintnostressin May 20, 2026
b0c6640
exhaustive pattern matching
aintnostressin May 20, 2026
a693e6f
treat only transport errors as infra errors
aintnostressin May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions examples/watcher/watcher-config.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
testnet = false
homeserver = "8um71us3fyw6h8wbcxb5ar3rwusy1a6u49956ikzojg3gcwd1dty"
events_limit = 50
key_based_events_limit = 50
watcher_sleep = 5000
hs_resolver_sleep = 10000
# Initial backoff duration (in seconds) after the first failure of a homeserver
initial_backoff_secs = 60
# Maximum backoff duration (in seconds) for a failing homeserver
Expand All @@ -18,6 +20,21 @@ moderated_tags = [
"il_adult_nu_sex_act",
]

# Event retry configuration
[retry]
# Transient error retry limit before dead-letter
max_retries = 10
# Safety net for homeservers that disappear silently
max_dependency_retries = 50
# Base for exponential backoff on transient retries (seconds)
initial_backoff_secs = 10
# Backoff ceiling for transient retries (seconds)
max_backoff_secs = 3600
# Base for MissingDependency polling backoff (seconds)
initial_missing_dep_backoff_secs = 60
# Backoff ceiling for MissingDependency (seconds)
max_missing_dep_backoff_secs = 3600

[stack]
# Logging, options: error, warn, info, debug and trace
log_level = "debug"
Expand Down
21 changes: 20 additions & 1 deletion nexus-common/default.config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ testnet = false
testnet_host = "localhost"
# Synonym homeserver pubky
homeserver = "8um71us3fyw6h8wbcxb5ar3rwusy1a6u49956ikzojg3gcwd1dty"
# Maximum number of events to fetch per run from each homeserver
# Maximum number of events to fetch per run from the default homeserver (max: 1000)
events_limit = 50
# Maximum events per user per run for key-based (non-default) homeservers (max: 100)
key_based_events_limit = 50
# Maximum number of monitored homeservers. If set to 1, only the default homeserver is monitored.
monitored_homeservers_limit = 50
watcher_sleep = 5000
hs_resolver_sleep = 10000
# Minimum time (ms) before a user's homeserver mapping is considered stale and therefore eligible to be re-resolved (default: 1 hour)
hs_resolver_ttl = 3600000
# Initial backoff duration (in seconds) after the first failure of a homeserver
initial_backoff_secs = 60
# Maximum backoff duration (in seconds) for a failing homeserver
Expand All @@ -35,6 +40,20 @@ moderated_tags = [
"il_adult_nu_sex_act",
]

# Event retry configuration
[watcher.retry]
# Transient error retry limit before dead-letter
max_retries = 10
# Safety net for homeservers that disappear silently
max_dependency_retries = 50
# Base for exponential backoff on transient retries (seconds)
initial_backoff_secs = 10
# Backoff ceiling for transient retries (seconds)
max_backoff_secs = 3600
# Base for MissingDependency polling backoff (seconds)
initial_missing_dep_backoff_secs = 60
# Backoff ceiling for MissingDependency (seconds)
max_missing_dep_backoff_secs = 3600

[stack]
# Logging, options: error, warn, info, debug and trace
Expand Down
2 changes: 2 additions & 0 deletions nexus-common/src/config/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ mod tests {
PubkyId::try_from("8um71us3fyw6h8wbcxb5ar3rwusy1a6u49956ikzojg3gcwd1dty").unwrap()
);
assert_eq!(c.watcher.events_limit, 50);
assert_eq!(c.watcher.key_based_events_limit, 50);
assert_eq!(c.watcher.watcher_sleep, 5_000);
assert_eq!(c.watcher.hs_resolver_sleep, 10_000);
assert_eq!(
c.watcher.moderation_id,
PubkyId::try_from(DEFAULT_MODERATION_ID).unwrap()
Expand Down
8 changes: 5 additions & 3 deletions nexus-common/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ mod api;
mod daemon;
pub mod file;
mod stack;
mod watcher;
pub mod watcher;

pub use api::ApiConfig;
pub use daemon::DaemonConfig;
pub use stack::{default_stack, OtlpConfig, StackConfig};
pub use watcher::WatcherConfig;
pub use watcher::{DEFAULT_INITIAL_BACKOFF_SECS, DEFAULT_MAX_BACKOFF_SECS};
pub use watcher::{
EventRetryConfig, WatcherConfig, DEFAULT_HS_RESOLVER_TTL, DEFAULT_INITIAL_BACKOFF_SECS,
DEFAULT_MAX_BACKOFF_SECS, MAX_EVENTS_LIMIT, MAX_KEY_BASED_EVENTS_LIMIT,
};

use crate::file::validate_and_expand_path;

Expand Down
163 changes: 159 additions & 4 deletions nexus-common/src/config/watcher.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
use crate::models::event::EventProcessorError;

use super::file::ConfigLoader;
use super::{default_stack, DaemonConfig, StackConfig};
use async_trait::async_trait;
use pubky_app_specs::PubkyId;
use serde::{Deserialize, Serialize};
use serde::{de::Error, Deserialize, Deserializer, Serialize};
use std::fmt::Debug;

pub const TESTNET: bool = false;
pub const DEFAULT_TESTNET_HOST: &str = "localhost";
// Testnet homeserver key
pub const HOMESERVER_PUBKY: &str = "8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo";
/// Default for [WatcherConfig::events_limit]
pub const DEFAULT_EVENTS_LIMIT: u32 = 1_000;
pub const DEFAULT_EVENTS_LIMIT: u16 = 1_000;
/// Default for [WatcherConfig::key_based_events_limit]
pub const DEFAULT_KEY_BASED_EVENTS_LIMIT: u16 = 50;
/// Upper bound for [WatcherConfig::events_limit]
pub const MAX_EVENTS_LIMIT: u16 = 1_000;
/// Upper bound for [WatcherConfig::key_based_events_limit]
pub const MAX_KEY_BASED_EVENTS_LIMIT: u16 = 100;
/// Default for [WatcherConfig::monitored_homeservers_limit]
pub const DEFAULT_MONITORED_HOMESERVERS_LIMIT: usize = 50;
/// Default for [WatcherConfig::watcher_sleep]
pub const DEFAULT_WATCHER_SLEEP: u64 = 5_000;
/// Default for [WatcherConfig::hs_resolver_sleep]
pub const DEFAULT_HS_RESOLVER_SLEEP: u64 = 10_000;
/// Default for [WatcherConfig::hs_resolver_ttl]: 1 hour in milliseconds
pub const DEFAULT_HS_RESOLVER_TTL: u64 = 3_600_000;
/// Default for [WatcherConfig::initial_backoff_secs]
pub const DEFAULT_INITIAL_BACKOFF_SECS: u64 = 60;
/// Default for [WatcherConfig::max_backoff_secs]
pub const DEFAULT_MAX_BACKOFF_SECS: u64 = 3_600;

// Retry configuration defaults
/// Default for [EventRetryConfig::max_retries]
pub const DEFAULT_MAX_RETRIES: u32 = 10;
/// Default for [EventRetryConfig::max_dependency_retries]
pub const DEFAULT_MAX_DEPENDENCY_RETRIES: u32 = 50;
/// Default for [EventRetryConfig::initial_backoff_secs] (transient errors)
pub const DEFAULT_INITIAL_TRANSIENT_BACKOFF_SECS: u64 = 10;
/// Default for [EventRetryConfig::max_backoff_secs] (transient errors)
pub const DEFAULT_MAX_TRANSIENT_BACKOFF_SECS: u64 = 3_600;
/// Default for [EventRetryConfig::initial_missing_dep_backoff_secs]
pub const DEFAULT_INITIAL_MISSING_DEP_BACKOFF_SECS: u64 = 60;
/// Default for [EventRetryConfig::max_missing_dep_backoff_secs]
pub const DEFAULT_MAX_MISSING_DEP_BACKOFF_SECS: u64 = 3_600;

// Default moderation service key (test user key, overridden by config.toml value)
pub const DEFAULT_MODERATION_ID: &str = "uo7jgkykft4885n8cruizwy6khw71mnu5pq3ay9i8pw1ymcn85ko";
// Moderation service key
Expand All @@ -31,19 +58,89 @@ pub const MODERATED_TAGS: [&str; 6] = [
"il_adult_nu_sex_act",
];

/// Retry configuration settings
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(default)]
pub struct EventRetryConfig {
/// Transient error retry limit before dead-letter
pub max_retries: u32,
/// Safety net for homeservers that disappear silently (no DEL events, content just gone)
pub max_dependency_retries: u32,
/// Base for exponential backoff on transient retries (seconds)
pub initial_backoff_secs: u64,
/// Backoff ceiling for transient retries (seconds)
pub max_backoff_secs: u64,
/// Base for MissingDependency polling backoff (seconds)
pub initial_missing_dep_backoff_secs: u64,
/// Backoff ceiling for MissingDependency (seconds)
pub max_missing_dep_backoff_secs: u64,
}

impl Default for EventRetryConfig {
fn default() -> Self {
Self {
max_retries: DEFAULT_MAX_RETRIES,
max_dependency_retries: DEFAULT_MAX_DEPENDENCY_RETRIES,
initial_backoff_secs: DEFAULT_INITIAL_TRANSIENT_BACKOFF_SECS,
max_backoff_secs: DEFAULT_MAX_TRANSIENT_BACKOFF_SECS,
initial_missing_dep_backoff_secs: DEFAULT_INITIAL_MISSING_DEP_BACKOFF_SECS,
max_missing_dep_backoff_secs: DEFAULT_MAX_MISSING_DEP_BACKOFF_SECS,
}
}
}

impl EventRetryConfig {
/// Returns (initial_backoff, max_backoff) values, in seconds, for the given error
pub fn get_backoff_params(&self, error: &EventProcessorError) -> (u64, u64) {
let initial = match error.is_missing_dependency() {
true => self.initial_missing_dep_backoff_secs,
false => self.initial_backoff_secs,
};
let max = match error.is_missing_dependency() {
true => self.max_missing_dep_backoff_secs,
false => self.max_backoff_secs,
};
(initial, max)
}
}

/// Configuration settings for the Nexus Watcher service
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct WatcherConfig {
pub testnet: bool,
pub testnet_host: String,

/// Default homeserver. Other homeservers may be ingested in addition, but this one is prioritized.
pub homeserver: PubkyId,
/// Maximum number of events to fetch per run from each homeserver
pub events_limit: u32,

/// Maximum number of events to fetch per run from the default homeserver.
/// Must not exceed [MAX_EVENTS_LIMIT].
#[serde(deserialize_with = "deserialize_events_limit")]
pub events_limit: u16,

/// Maximum events per user per run for key-based (non-default) homeservers.
/// Must not exceed [MAX_KEY_BASED_EVENTS_LIMIT].
#[serde(
default = "default_key_based_events_limit",
deserialize_with = "deserialize_key_based_events_limit"
)]
pub key_based_events_limit: u16,

/// Maximum number of monitored homeservers
pub monitored_homeservers_limit: usize,

/// Sleep between every full run (over all monitored homeservers), in milliseconds
pub watcher_sleep: u64,

/// Sleep between every run of the user HS resolver periodic task, in milliseconds
#[serde(default = "default_hs_resolver_sleep")]
pub hs_resolver_sleep: u64,

/// Minimum time (ms) before a user's homeserver mapping is re-resolved.
/// Users whose `HOSTED_BY.resolved_at` is newer than this TTL are skipped.
#[serde(default = "default_hs_resolver_ttl")]
pub hs_resolver_ttl: u64,

/// Initial backoff duration (in seconds) after the first failure of a homeserver
#[serde(default = "default_initial_backoff_secs")]
pub initial_backoff_secs: u64,
Expand All @@ -52,6 +149,11 @@ pub struct WatcherConfig {
pub max_backoff_secs: u64,
#[serde(default = "default_stack")]
pub stack: StackConfig,

// Retry configuration
#[serde(default)]
pub retry: EventRetryConfig,

// Moderation
pub moderation_id: PubkyId,
pub moderated_tags: Vec<String>,
Expand All @@ -72,16 +174,69 @@ impl Default for WatcherConfig {
testnet_host: DEFAULT_TESTNET_HOST.to_string(),
homeserver,
events_limit: DEFAULT_EVENTS_LIMIT,
key_based_events_limit: DEFAULT_KEY_BASED_EVENTS_LIMIT,
monitored_homeservers_limit: DEFAULT_MONITORED_HOMESERVERS_LIMIT,
watcher_sleep: DEFAULT_WATCHER_SLEEP,
hs_resolver_sleep: DEFAULT_HS_RESOLVER_SLEEP,
hs_resolver_ttl: DEFAULT_HS_RESOLVER_TTL,
initial_backoff_secs: DEFAULT_INITIAL_BACKOFF_SECS,
max_backoff_secs: DEFAULT_MAX_BACKOFF_SECS,
retry: EventRetryConfig::default(),
moderation_id,
moderated_tags: MODERATED_TAGS.iter().map(|s| s.to_string()).collect(),
}
}
}

fn default_hs_resolver_sleep() -> u64 {
DEFAULT_HS_RESOLVER_SLEEP
}

fn default_key_based_events_limit() -> u16 {
DEFAULT_KEY_BASED_EVENTS_LIMIT
}

fn deserialize_events_limit<'de, D>(deserializer: D) -> Result<u16, D::Error>
where
D: Deserializer<'de>,
{
let val = u16::deserialize(deserializer)?;
if val == 0 {
return Err(D::Error::custom("events_limit must be at least 1"));
}

if val > MAX_EVENTS_LIMIT {
let err_msg = format!("events_limit ({val}) exceeds max ({MAX_EVENTS_LIMIT})");
Err(D::Error::custom(err_msg))
} else {
Ok(val)
}
}

fn deserialize_key_based_events_limit<'de, D>(deserializer: D) -> Result<u16, D::Error>
where
D: Deserializer<'de>,
{
let val = u16::deserialize(deserializer)?;
if val == 0 {
return Err(D::Error::custom(
"key_based_events_limit must be at least 1",
));
}

if val > MAX_KEY_BASED_EVENTS_LIMIT {
let err_msg =
format!("key_based_events_limit ({val}) exceeds max ({MAX_KEY_BASED_EVENTS_LIMIT})");
Err(D::Error::custom(err_msg))
} else {
Ok(val)
}
}

fn default_hs_resolver_ttl() -> u64 {
DEFAULT_HS_RESOLVER_TTL
}

/// Converts a [`DaemonConfig`] into an [`WatcherConfig`], extracting only the Watcher-related settings
/// and the shared application stack
impl From<DaemonConfig> for WatcherConfig {
Expand Down
Loading
Loading