Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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,739 changes: 1,739 additions & 0 deletions src/app.rs

Large diffs are not rendered by default.

25 changes: 7 additions & 18 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,15 @@ use tokio::sync::Semaphore;
use tracing::debug;
use zeroize::Zeroizing;

use crate::crypto::nip59::DEFAULT_MAX_TOKENS_PER_EVENT;
use crate::error::Result;
use crate::nostr::events::{
DEFAULT_DEDUP_RETENTION_SECS, DEFAULT_MAX_NOTIFICATION_AGE_SECS,
use crate::defaults::{
DEFAULT_DEDUP_RETENTION_SECS, DEFAULT_GLOBAL_UNWRAP_LIMIT_PER_HOUR,
DEFAULT_GLOBAL_UNWRAP_LIMIT_PER_MINUTE, DEFAULT_MAX_CONCURRENT_EVENT_PROCESSING,
DEFAULT_MAX_DEDUP_CACHE_SIZE, DEFAULT_MAX_NOTIFICATION_AGE_SECS,
DEFAULT_MAX_NOTIFICATION_FUTURE_SKEW_SECS,
DEFAULT_MAX_SIZE as DEFAULT_MAX_RATE_LIMIT_CACHE_SIZE, DEFAULT_MAX_TOKENS_PER_EVENT,
DEFAULT_RATE_LIMIT_PER_HOUR, DEFAULT_RATE_LIMIT_PER_MINUTE,
};
use crate::rate_limiter::{
DEFAULT_GLOBAL_UNWRAP_LIMIT_PER_HOUR, DEFAULT_GLOBAL_UNWRAP_LIMIT_PER_MINUTE,
DEFAULT_MAX_SIZE as DEFAULT_MAX_RATE_LIMIT_CACHE_SIZE, DEFAULT_RATE_LIMIT_PER_HOUR,
DEFAULT_RATE_LIMIT_PER_MINUTE,
};
use crate::error::Result;

/// Root configuration structure.
#[derive(Debug, Clone, Deserialize)]
Expand Down Expand Up @@ -79,18 +77,9 @@ pub struct AppConfig {
pub glitchtip: GlitchtipConfig,
}

/// Default maximum size for the deduplication cache.
const DEFAULT_MAX_DEDUP_CACHE_SIZE: usize = 100_000;
const DEFAULT_HEALTH_BIND_ADDRESS: &str = "127.0.0.1:8080";
const ENV_PREFIX: &str = "TRANSPONDER_";

/// Default maximum number of events processed concurrently.
///
/// Caps the total in-flight gift-wrap unwrap (ECDH) work so a flood cannot
/// spawn unbounded crypto tasks, while still draining the relay broadcast
/// channel quickly enough to avoid `Lagged` overflow.
const DEFAULT_MAX_CONCURRENT_EVENT_PROCESSING: usize = 64;

fn default_max_dedup_cache_size() -> usize {
DEFAULT_MAX_DEDUP_CACHE_SIZE
}
Expand Down
3 changes: 1 addition & 2 deletions src/crypto/nip59.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ const TAG_ENCODING: &str = "encoding";
const VERSION_MIP05_V1: &str = "mip05-v1";
const ENCODING_BASE64: &str = "base64";

/// Default maximum number of encrypted tokens accepted in one notification event.
pub const DEFAULT_MAX_TOKENS_PER_EVENT: usize = 100;
pub use crate::defaults::DEFAULT_MAX_TOKENS_PER_EVENT;

/// Maximum number of characters of attacker-controlled tag content included in
/// error messages.
Expand Down
60 changes: 60 additions & 0 deletions src/defaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! Centralized default values for configuration and runtime components.
//!
//! Single source of truth for serde defaults in [`crate::config`] and for
//! runtime components that share the same tuning knobs.

use nostr_sdk::prelude::nip59;

/// Maximum NIP-59 timestamp randomization window for gift wraps (seconds).
pub const NIP59_TIMESTAMP_TWEAK_WINDOW_SECS: u64 = nip59::RANGE_RANDOM_TIMESTAMP_TWEAK.end;

// === Rate limiting ===

/// Default maximum cache size (100,000 entries).
pub const DEFAULT_MAX_SIZE: usize = 100_000;

/// Default rate limit per minute (240 = 4 per second).
pub const DEFAULT_RATE_LIMIT_PER_MINUTE: u32 = 240;

/// Default rate limit per hour.
pub const DEFAULT_RATE_LIMIT_PER_HOUR: u32 = 5000;

/// Default global pre-unwrap admission limit per minute.
pub const DEFAULT_GLOBAL_UNWRAP_LIMIT_PER_MINUTE: u32 = 600;

/// Default global pre-unwrap admission limit per hour.
pub const DEFAULT_GLOBAL_UNWRAP_LIMIT_PER_HOUR: u32 = 30_000;

// === Event processing / replay protection ===

/// Default maximum size for the volatile deduplication cache.
pub const DEFAULT_MAX_DEDUP_CACHE_SIZE: usize = 100_000;

/// Default maximum age for the unwrapped kind:446 notification request rumor.
pub const DEFAULT_MAX_NOTIFICATION_AGE_SECS: u64 = 3_600;

/// Default tolerated clock skew for future-dated notification rumors.
pub const DEFAULT_MAX_NOTIFICATION_FUTURE_SKEW_SECS: u64 = 300;

/// Default duration to keep processed gift-wrap event IDs in replay state.
pub const DEFAULT_DEDUP_RETENTION_SECS: u64 =
NIP59_TIMESTAMP_TWEAK_WINDOW_SECS + DEFAULT_MAX_NOTIFICATION_FUTURE_SKEW_SECS;

/// Default maximum number of encrypted tokens accepted in one notification event.
pub const DEFAULT_MAX_TOKENS_PER_EVENT: usize = 100;

/// Default maximum number of events processed concurrently.
pub const DEFAULT_MAX_CONCURRENT_EVENT_PROCESSING: usize = 64;

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn dedup_retention_matches_nip59_window_plus_skew() {
assert_eq!(
DEFAULT_DEDUP_RETENTION_SECS,
NIP59_TIMESTAMP_TWEAK_WINDOW_SECS + DEFAULT_MAX_NOTIFICATION_FUTURE_SKEW_SECS
);
}
}
27 changes: 27 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! Transponder - MIP-05 Push Notification Server
//!
//! A privacy-preserving push notification server implementing the Marmot MIP-05
//! specification.

pub mod app;
pub mod config;
pub mod crypto;
pub mod defaults;
pub mod error;
pub mod metrics;
pub mod nostr;
pub mod push;
pub mod rate_limiter;
pub mod redaction;
pub mod server;
pub mod shutdown;
pub mod telemetry;

#[cfg(test)]
pub(crate) mod test_metrics;
#[cfg(test)]
pub(crate) mod test_support;
#[cfg(test)]
pub(crate) mod test_vectors;

pub use app::run;
Loading