Compare commits

..

4 Commits

Author SHA1 Message Date
durch 70fb7d75d6 Split daily stats queries 2025-07-25 12:07:42 +02:00
durch f11b2caeb1 No error swallowing 2025-07-25 11:43:05 +02:00
durch 1a07dbb09c Bump cutoff 2025-07-25 10:18:35 +02:00
durch 9587247536 Time and id header middleware 2025-07-24 14:35:57 +02:00
59 changed files with 1169 additions and 2635 deletions
Generated
+54 -232
View File
@@ -1447,7 +1447,7 @@ checksum = "c2895653b4d9f1538a83970077cb01dfc77a4810524e51a110944688e916b18e"
dependencies = [
"prost 0.11.9",
"prost-types",
"tonic 0.9.2",
"tonic",
"tracing-core",
]
@@ -1469,7 +1469,7 @@ dependencies = [
"thread_local",
"tokio",
"tokio-stream",
"tonic 0.9.2",
"tonic",
"tracing",
"tracing-core",
"tracing-subscriber",
@@ -3580,7 +3580,6 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2 0.4.11",
"http 1.3.1",
"http-body 1.0.1",
"httparse",
@@ -3635,19 +3634,6 @@ dependencies = [
"tokio-io-timeout",
]
[[package]]
name = "hyper-timeout"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [
"hyper 1.6.0",
"hyper-util",
"pin-project-lite",
"tokio",
"tower-service",
]
[[package]]
name = "hyper-util"
version = "0.1.15"
@@ -5171,7 +5157,7 @@ dependencies = [
"clap_complete_fig",
"const-str",
"log",
"opentelemetry 0.30.0",
"opentelemetry",
"opentelemetry-jaeger",
"schemars 0.8.22",
"serde",
@@ -6015,10 +6001,6 @@ dependencies = [
"nym-validator-client 0.1.0",
"nym-wireguard",
"nym-wireguard-types 0.1.0",
"opentelemetry 0.30.0",
"opentelemetry-otlp",
"opentelemetry-stdout",
"opentelemetry_sdk 0.30.0",
"rand 0.8.5",
"sha2 0.10.9",
"sqlx",
@@ -6029,7 +6011,6 @@ dependencies = [
"tokio-tungstenite",
"tokio-util",
"tracing",
"tracing-opentelemetry",
"url",
"zeroize",
]
@@ -6088,7 +6069,6 @@ dependencies = [
"nym-sphinx 0.1.0",
"nym-statistics-common 0.1.0",
"nym-task 0.1.0",
"opentelemetry 0.30.0",
"rand 0.8.5",
"serde",
"serde_json",
@@ -6640,7 +6620,6 @@ dependencies = [
"cargo_metadata 0.19.2",
"celes",
"chacha",
"chrono",
"clap",
"colored",
"criterion",
@@ -6687,11 +6666,6 @@ dependencies = [
"nym-verloc",
"nym-wireguard",
"nym-wireguard-types 0.1.0",
"opentelemetry 0.30.0",
"opentelemetry-otlp",
"opentelemetry-semantic-conventions 0.30.0",
"opentelemetry-stdout",
"opentelemetry_sdk 0.30.0",
"rand 0.8.5",
"rand_chacha 0.3.1",
"serde",
@@ -6705,10 +6679,7 @@ dependencies = [
"toml 0.8.23",
"tower-http 0.5.2",
"tracing",
"tracing-core",
"tracing-indicatif",
"tracing-opentelemetry",
"tracing-serde",
"tracing-subscriber",
"url",
"utoipa",
@@ -6797,7 +6768,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "3.2.2"
version = "3.2.6"
dependencies = [
"ammonia",
"anyhow",
@@ -6809,6 +6780,7 @@ dependencies = [
"cosmwasm-std",
"envy",
"futures-util",
"hex",
"itertools 0.14.0",
"moka",
"nym-bin-common 0.6.0 (git+https://github.com/nymtech/nym.git?branch=release/2025.11-cheddar)",
@@ -7089,11 +7061,6 @@ dependencies = [
"nym-task 0.1.0",
"nym-topology 0.1.0",
"nym-validator-client 0.1.0",
"opentelemetry 0.30.0",
"opentelemetry-otlp",
"opentelemetry-semantic-conventions 0.30.0",
"opentelemetry-stdout",
"opentelemetry_sdk 0.30.0",
"parking_lot",
"rand 0.8.5",
"reqwest 0.12.22",
@@ -7107,8 +7074,6 @@ dependencies = [
"tokio-util",
"toml 0.8.23",
"tracing",
"tracing-core",
"tracing-opentelemetry",
"tracing-subscriber",
"url",
"uuid",
@@ -8270,164 +8235,90 @@ dependencies = [
[[package]]
name = "opentelemetry"
version = "0.23.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b69a91d4893e713e06f724597ad630f1fa76057a5e1026c0ca67054a9032a76"
checksum = "5f4b8347cc26099d3aeee044065ecc3ae11469796b4d65d065a23a584ed92a6f"
dependencies = [
"futures-core",
"futures-sink",
"js-sys",
"once_cell",
"pin-project-lite",
"thiserror 1.0.69",
]
[[package]]
name = "opentelemetry"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf416e4cb72756655126f7dd7bb0af49c674f4c1b9903e80c009e0c37e552e6"
dependencies = [
"futures-core",
"futures-sink",
"js-sys",
"pin-project-lite",
"thiserror 2.0.12",
"tracing",
"opentelemetry_api",
"opentelemetry_sdk",
]
[[package]]
name = "opentelemetry-http"
version = "0.12.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0ba633e55c5ea6f431875ba55e71664f2fa5d3a90bd34ec9302eecc41c865dd"
checksum = "a819b71d6530c4297b49b3cae2939ab3a8cc1b9f382826a1bc29dd0ca3864906"
dependencies = [
"async-trait",
"bytes",
"http 0.2.12",
"isahc",
"opentelemetry 0.23.0",
]
[[package]]
name = "opentelemetry-http"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f6639e842a97dbea8886e3439710ae463120091e2e064518ba8e716e6ac36d"
dependencies = [
"async-trait",
"bytes",
"http 1.3.1",
"opentelemetry 0.30.0",
"reqwest 0.12.22",
"opentelemetry_api",
]
[[package]]
name = "opentelemetry-jaeger"
version = "0.22.0"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501b471b67b746d9a07d4c29f8be00f952d1a2eca356922ede0098cbaddff19f"
checksum = "08e028dc9f4f304e9320ce38c80e7cf74067415b1ad5a8750a38bae54a4d450d"
dependencies = [
"async-trait",
"futures-core",
"futures-util",
"futures",
"futures-executor",
"http 0.2.12",
"isahc",
"opentelemetry 0.23.0",
"opentelemetry-http 0.12.0",
"opentelemetry-semantic-conventions 0.15.0",
"opentelemetry_sdk 0.23.0",
"once_cell",
"opentelemetry",
"opentelemetry-http",
"opentelemetry-semantic-conventions",
"thiserror 1.0.69",
"thrift",
"tokio",
]
[[package]]
name = "opentelemetry-otlp"
version = "0.30.0"
name = "opentelemetry-semantic-conventions"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbee664a43e07615731afc539ca60c6d9f1a9425e25ca09c57bc36c87c55852b"
checksum = "24e33428e6bf08c6f7fcea4ddb8e358fab0fe48ab877a87c70c6ebe20f673ce5"
dependencies = [
"http 1.3.1",
"opentelemetry 0.30.0",
"opentelemetry-http 0.30.0",
"opentelemetry-proto",
"opentelemetry_sdk 0.30.0",
"prost 0.13.5",
"reqwest 0.12.22",
"thiserror 2.0.12",
"tokio",
"tonic 0.13.1",
"tracing",
"opentelemetry",
]
[[package]]
name = "opentelemetry-proto"
version = "0.30.0"
name = "opentelemetry_api"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e046fd7660710fe5a05e8748e70d9058dc15c94ba914e7c4faa7c728f0e8ddc"
checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2"
dependencies = [
"opentelemetry 0.30.0",
"opentelemetry_sdk 0.30.0",
"prost 0.13.5",
"tonic 0.13.1",
]
[[package]]
name = "opentelemetry-semantic-conventions"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1869fb4bb9b35c5ba8a1e40c9b128a7b4c010d07091e864a29da19e4fe2ca4d7"
[[package]]
name = "opentelemetry-semantic-conventions"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83d059a296a47436748557a353c5e6c5705b9470ef6c95cfc52c21a8814ddac2"
[[package]]
name = "opentelemetry-stdout"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447191061af41c3943e082ea359ab8b64ff27d6d34d30d327df309ddef1eef6f"
dependencies = [
"chrono",
"opentelemetry 0.30.0",
"opentelemetry_sdk 0.30.0",
"fnv",
"futures-channel",
"futures-util",
"indexmap 1.9.3",
"once_cell",
"pin-project-lite",
"thiserror 1.0.69",
"urlencoding",
]
[[package]]
name = "opentelemetry_sdk"
version = "0.23.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae312d58eaa90a82d2e627fd86e075cf5230b3f11794e2ed74199ebbe572d4fd"
checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1"
dependencies = [
"async-trait",
"crossbeam-channel",
"dashmap",
"fnv",
"futures-channel",
"futures-executor",
"futures-util",
"lazy_static",
"once_cell",
"opentelemetry 0.23.0",
"ordered-float 4.6.0",
"opentelemetry_api",
"percent-encoding",
"rand 0.8.5",
"thiserror 1.0.69",
]
[[package]]
name = "opentelemetry_sdk"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11f644aa9e5e31d11896e024305d7e3c98a88884d9f8919dbf37a9991bc47a4b"
dependencies = [
"futures-channel",
"futures-executor",
"futures-util",
"opentelemetry 0.30.0",
"percent-encoding",
"rand 0.9.2",
"serde_json",
"thiserror 2.0.12",
"tokio",
"tokio-stream",
]
@@ -8447,15 +8338,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "ordered-float"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951"
dependencies = [
"num-traits",
]
[[package]]
name = "overload"
version = "0.1.1"
@@ -9373,7 +9255,6 @@ dependencies = [
"async-compression",
"base64 0.22.1",
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"http 1.3.1",
@@ -11151,7 +11032,7 @@ dependencies = [
"byteorder",
"integer-encoding",
"log",
"ordered-float 2.10.1",
"ordered-float",
"threadpool",
]
@@ -11457,7 +11338,7 @@ dependencies = [
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.32",
"hyper-timeout 0.4.1",
"hyper-timeout",
"percent-encoding",
"pin-project",
"prost 0.11.9",
@@ -11469,34 +11350,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "tonic"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9"
dependencies = [
"async-trait",
"base64 0.22.1",
"bytes",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"hyper 1.6.0",
"hyper-timeout 0.5.2",
"hyper-util",
"percent-encoding",
"pin-project",
"prost 0.13.5",
"tokio",
"tokio-rustls 0.26.2",
"tokio-stream",
"tower 0.5.2",
"tower-layer",
"tower-service",
"tracing",
"webpki-roots 0.26.11",
]
[[package]]
name = "tower"
version = "0.4.13"
@@ -11525,12 +11378,9 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"indexmap 2.10.0",
"pin-project-lite",
"slab",
"sync_wrapper 1.0.2",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
@@ -11672,30 +11522,16 @@ dependencies = [
[[package]]
name = "tracing-opentelemetry"
version = "0.31.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddcf5959f39507d0d04d6413119c04f33b623f4f951ebcbdddddfad2d0623a9c"
checksum = "00a39dcf9bfc1742fa4d6215253b33a6e474be78275884c216fc2a06267b3600"
dependencies = [
"js-sys",
"once_cell",
"opentelemetry 0.30.0",
"opentelemetry_sdk 0.30.0",
"smallvec",
"opentelemetry",
"tracing",
"tracing-core",
"tracing-log 0.2.0",
"tracing-log 0.1.4",
"tracing-subscriber",
"web-time",
]
[[package]]
name = "tracing-serde"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
dependencies = [
"serde",
"tracing-core",
]
[[package]]
@@ -11708,15 +11544,12 @@ dependencies = [
"nu-ansi-term",
"once_cell",
"regex",
"serde",
"serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log 0.2.0",
"tracing-serde",
]
[[package]]
@@ -12114,6 +11947,12 @@ dependencies = [
"serde",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf-8"
version = "0.7.6"
@@ -13381,20 +13220,3 @@ dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "zulip-client"
version = "0.1.0"
dependencies = [
"itertools 0.14.0",
"nym-bin-common 0.6.0",
"nym-http-api-client 0.1.0",
"reqwest 0.12.22",
"serde",
"serde_json",
"thiserror 2.0.12",
"tokio",
"tracing",
"url",
"zeroize",
]
+4 -10
View File
@@ -99,7 +99,7 @@ members = [
"common/wasm/storage",
"common/wasm/utils",
"common/wireguard",
"common/wireguard-types", "common/zulip-client",
"common/wireguard-types",
"documentation/autodoc",
"gateway",
"nym-api",
@@ -281,12 +281,8 @@ nix = "0.27.1"
notify = "5.1.0"
okapi = "0.7.0"
once_cell = "1.21.3"
opentelemetry = "0.30.0"
opentelemetry-otlp = "0.30.0"
opentelemetry-semantic-conventions = "0.30.0"
opentelemetry_sdk = "0.30.0"
opentelemetry-stdout = "0.30.0"
opentelemetry-jaeger = "0.22.0"
opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0"
parking_lot = "0.12.3"
pem = "0.8"
petgraph = "0.6.5"
@@ -342,10 +338,8 @@ toml = "0.8.22"
tower = "0.5.2"
tower-http = "0.5.2"
tracing = "0.1.41"
tracing-core = "0.1.33"
tracing-log = "0.2"
tracing-opentelemetry = "0.31.0"
tracing-serde = "0.2.0"
tracing-opentelemetry = "0.19.0"
tracing-subscriber = "0.3.19"
tracing-tree = "0.2.2"
tracing-indicatif = "0.3.9"
-11
View File
@@ -1,11 +0,0 @@
find . -name "Cargo.toml" -not -path "./target/*" -exec grep -l "^name = " {} \; | xargs grep "^name = " | grep -v 'name = "nym-'
find . -name "Cargo.toml" \
-not -path "./nym-wallet/*" \
-not -path "./contracts/*" \
-not -path "./target/*" \
-not -name "./Cargo.toml" \
-exec sed -i 's/^version = ".*"/version.workspace = true/' {} \;
find . -name "Cargo.toml" -not -path "./target/*" -exec awk '/^\[package\]/{flag=1; next} flag && /^name = /{print FILENAME ":" $0; flag=0} /^\[/{flag=0}' {} \; | grep -v 'name = "nym-'
+2 -2
View File
@@ -21,10 +21,10 @@ serde_json = { workspace = true, optional = true }
tracing-subscriber = { workspace = true, features = ["env-filter"], optional = true }
tracing-tree = { workspace = true, optional = true }
tracing = { workspace = true, optional = true }
opentelemetry-jaeger = { workspace = true, features = ["tokio", "collector_client", "isahc_collector_client"], optional = true }
opentelemetry-jaeger = { workspace = true, features = ["rt-tokio", "collector_client", "isahc_collector_client"], optional = true }
tracing-opentelemetry = { workspace = true, optional = true }
utoipa = { workspace = true, optional = true }
opentelemetry = { workspace = true, optional = true }
opentelemetry = { workspace = true, features = ["rt-tokio"], optional = true }
[build-dependencies]
@@ -65,7 +65,7 @@ pub trait MixnetClientStorage {
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
}
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct Ephemeral {
key_store: InMemEphemeralKeys,
reply_store: reply_storage::Empty,
@@ -22,7 +22,7 @@ mod test;
// use the old key after new one was issued.
// Remember that Arc<T> has Deref implementation for T
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct ClientKeys {
/// identity key associated with the client instance.
identity_keypair: Arc<ed25519::KeyPair>,
@@ -196,7 +196,7 @@ impl KeyStore for OnDiskKeys {
}
}
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct InMemEphemeralKeys {
keys: Arc<Mutex<ClientKeys>>,
}
+1 -7
View File
@@ -53,7 +53,7 @@ pub enum Ed25519RecoveryError {
}
/// Keypair for usage in ed25519 EdDSA.
#[derive(Zeroize, ZeroizeOnDrop)]
#[derive(Debug, Zeroize, ZeroizeOnDrop)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct KeyPair {
private_key: PrivateKey,
@@ -66,12 +66,6 @@ pub struct KeyPair {
index: u32,
}
impl Debug for KeyPair {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.public_key.to_base58_string(), f)
}
}
/// All keys will always have an index field populated this is to prevent anyone from figuring out if
/// the keys are derived or random, and alter their behaviour based on that.
impl KeyPair {
@@ -55,12 +55,6 @@ pub struct KeyPair {
pub(crate) public_key: PublicKey,
}
impl Debug for KeyPair {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.public_key.to_base58_string(), f)
}
}
impl KeyPair {
#[cfg(feature = "rand")]
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
@@ -233,12 +227,6 @@ impl AsRef<[u8]> for PublicKey {
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct PrivateKey(x25519_dalek::StaticSecret);
impl Debug for PrivateKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_base58_string())
}
}
impl Display for PrivateKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_base58_string())
+1 -4
View File
@@ -19,6 +19,7 @@ serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
strum = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true, features = ["log"] }
time = { workspace = true }
subtle = { workspace = true }
zeroize = { workspace = true }
@@ -33,9 +34,6 @@ nym-task = { path = "../task" }
nym-credentials = { path = "../credentials" }
nym-credentials-interface = { path = "../credentials-interface" }
opentelemetry = { workspace = true, features = ["trace"] }
tracing = { workspace = true, features = ["std", "attributes", "tracing-attributes"] }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
workspace = true
features = ["time"]
@@ -50,4 +48,3 @@ default-features = false
[dev-dependencies]
nym-compact-ecash = { path = "../nym_offline_compact_ecash" } # we need specific imports in tests
@@ -20,7 +20,7 @@ use rand::{thread_rng, CryptoRng, RngCore};
use std::any::{type_name, Any};
use std::str::FromStr;
use std::time::Duration;
use tracing::{error, trace};
use tracing::log::*;
use tungstenite::Message as WsMessage;
#[cfg(not(target_arch = "wasm32"))]
@@ -16,9 +16,6 @@ pub struct AuthenticateRequest {
pub content: AuthenticateRequestContent,
pub request_signature: ed25519::Signature,
#[serde(default)]
pub debug_trace_id: Option<String>,
}
impl AuthenticateRequest {
@@ -26,7 +23,6 @@ impl AuthenticateRequest {
protocol_version: u8,
shared_key: &SharedGatewayKey,
identity_keys: &ed25519::KeyPair,
debug_trace_id: Option<String>,
) -> Result<AuthenticateRequest, GatewayRequestsError> {
let content = AuthenticateRequestContent::new(
protocol_version,
@@ -39,7 +35,6 @@ impl AuthenticateRequest {
Ok(AuthenticateRequest {
content,
request_signature,
debug_trace_id,
})
}
@@ -12,10 +12,8 @@ use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::ed25519;
use nym_sphinx::DestinationAddressBytes;
use nym_statistics_common::types::SessionType;
use opentelemetry::trace::TraceContextExt;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use tracing::{instrument, warn};
use tungstenite::Message;
pub mod authenticate;
@@ -78,10 +76,6 @@ pub enum ClientControlRequest {
address: String,
enc_address: String,
iv: String,
/// this is a trace id that is used in testing and performance verification
/// in mainnet, this will always be set to None
#[serde(default)]
debug_trace_id: Option<String>,
},
AuthenticateV2(Box<AuthenticateRequest>),
@@ -133,24 +127,14 @@ impl ClientControlRequest {
let nonce = shared_key.random_nonce_or_iv();
let ciphertext = shared_key.encrypt_naive(address.as_bytes_ref(), Some(&nonce))?;
let otel_context = opentelemetry::Context::current();
warn!("OTEL CONTEXT: {:?}", otel_context);
let span = otel_context.span();
let context = span.span_context();
let trace_id = context.trace_id();
warn!("TRACE_ID: {:?}", trace_id);
// panic!();
Ok(ClientControlRequest::Authenticate {
protocol_version,
address: address.as_base58_string(),
enc_address: bs58::encode(&ciphertext).into_string(),
iv: bs58::encode(&nonce).into_string(),
debug_trace_id: Some(trace_id.to_string()),
})
}
#[instrument]
pub fn new_authenticate_v2(
shared_key: &SharedGatewayKey,
identity_keys: &ed25519::KeyPair,
@@ -158,21 +142,8 @@ impl ClientControlRequest {
// if we're using v2 authentication, we must announce at least that protocol version
let protocol_version = AUTHENTICATE_V2_PROTOCOL_VERSION;
let otel_context = opentelemetry::Context::current();
warn!("OTEL CONTEXT: {:?}", otel_context);
let span = otel_context.span();
let context = span.span_context();
let trace_id = context.trace_id();
warn!("TRACE_ID: {:?}", trace_id);
// panic!();
Ok(ClientControlRequest::AuthenticateV2(Box::new(
AuthenticateRequest::new(
protocol_version,
shared_key,
identity_keys,
Some(trace_id.to_string()),
)?,
AuthenticateRequest::new(protocol_version, shared_key, identity_keys)?,
)))
}
+28 -57
View File
@@ -6,9 +6,9 @@
//! The resolver itself is the set combination of the google, cloudflare, and quad9 endpoints
//! supporting DoH and DoT.
//!
//! This resolver supports a fallback mechanism where, should the DNS-over-TLS resolution fail, a
//! This resolver implements a fallback mechanism where, should the DNS-over-TLS resolution fail, a
//! followup resolution will be done using the hosts configured default (e.g. `/etc/resolve.conf` on
//! linux). This is disabled by default and can be enabled using [`enable_system_fallback`].
//! linux).
//!
//! Requires the `dns-over-https-rustls`, `webpki-roots` feature for the
//! `hickory-resolver` crate
@@ -93,14 +93,14 @@ pub struct HickoryDnsResolver {
// Tokio Runtime in initialization, so we must delay the actual
// construction of the resolver.
state: Arc<OnceCell<TokioResolver>>,
fallback: Option<Arc<OnceCell<TokioResolver>>>,
fallback: Arc<OnceCell<TokioResolver>>,
dont_use_shared: bool,
}
impl Resolve for HickoryDnsResolver {
fn resolve(&self, name: Name) -> Resolving {
let resolver = self.state.clone();
let maybe_fallback = self.fallback.clone();
let fallback = self.fallback.clone();
let independent = self.dont_use_shared;
Box::pin(async move {
let resolver = resolver.get_or_try_init(|| {
@@ -117,30 +117,23 @@ impl Resolve for HickoryDnsResolver {
let lookup = match resolver.lookup_ip(name.as_str()).await {
Ok(res) => res,
Err(e) => {
if let Some(ref fallback) = maybe_fallback {
// on failure use the fall back system configured DNS resolver
if !e.is_no_records_found() {
warn!("primary DNS failed w/ error {e}: using system fallback");
}
let resolver = fallback.get_or_try_init(|| {
// using a closure here is slightly gross, but this makes sure that if the
// lazy-init returns an error it can be handled by the client
if independent {
new_resolver_system()
} else {
Ok(SHARED_RESOLVER
.fallback
.as_ref()
.ok_or(e)? // if the shared resolver has no fallback return the original error
.get_or_try_init(new_resolver_system)?
.clone())
}
})?;
resolver.lookup_ip(name.as_str()).await?
} else {
return Err(e.into());
// on failure use the fall back system configured DNS resolver
if !e.is_no_records_found() {
warn!("primary DNS failed w/ error {e}: using system fallback");
}
let resolver = fallback.get_or_try_init(|| {
// using a closure here is slightly gross, but this makes sure that if the
// lazy-init returns an error it can be handled by the client
if independent {
new_resolver_system()
} else {
Ok(SHARED_RESOLVER
.fallback
.get_or_try_init(new_resolver_system)?
.clone())
}
})?;
resolver.lookup_ip(name.as_str()).await?
}
};
@@ -169,17 +162,14 @@ impl HickoryDnsResolver {
let lookup = match resolver.lookup_ip(name).await {
Ok(res) => res,
Err(e) => {
if let Some(ref fallback) = self.fallback {
// on failure use the fall back system configured DNS resolver
if !e.is_no_records_found() {
warn!("primary DNS failed w/ error {e}: using system fallback");
}
let resolver = fallback.get_or_try_init(|| self.new_resolver_system())?;
resolver.lookup_ip(name).await?
} else {
return Err(e.into());
// on failure use the fall back system configured DNS resolver
if !e.is_no_records_found() {
warn!("primary DNS failed w/ error {e}: using system fallback");
}
let resolver = self
.fallback
.get_or_try_init(|| self.new_resolver_system())?;
resolver.lookup_ip(name).await?
}
};
@@ -203,34 +193,15 @@ impl HickoryDnsResolver {
}
fn new_resolver_system(&self) -> Result<TokioResolver, HickoryDnsError> {
if self.dont_use_shared || SHARED_RESOLVER.fallback.is_none() {
if self.dont_use_shared {
new_resolver_system()
} else {
Ok(SHARED_RESOLVER
.fallback
.as_ref()
.unwrap()
.get_or_try_init(new_resolver_system)?
.clone())
}
}
/// Enable fallback to the system default resolver if the primary (DoX) resolver fails
pub fn enable_system_fallback(&mut self) -> Result<(), HickoryDnsError> {
self.fallback = Some(Default::default());
let _ = self
.fallback
.as_ref()
.unwrap()
.get_or_try_init(new_resolver_system)?;
Ok(())
}
/// Disable fallback resolution. If the primary resolver fails the error is
/// returned immediately
pub fn disable_system_fallback(&mut self) {
self.fallback = None;
}
}
/// Create a new resolver with a custom DoT based configuration. The options are overridden to look
+1 -1
View File
@@ -16,7 +16,7 @@ pub struct UserAgent {
pub version: String,
/// client platform
pub platform: String,
/// source commit version for the calling crate / subsystem
/// source commit version for the calling calling crate / subsystem
pub git_commit: String,
}
+1 -1
View File
@@ -12,7 +12,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Zeroize, ZeroizeOnDrop)]
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct AckKey(CipherKey<AckEncryptionAlgorithm>);
#[derive(Debug)]
-31
View File
@@ -1,31 +0,0 @@
[package]
name = "zulip-client"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
readme.workspace = true
[dependencies]
thiserror = { workspace = true }
itertools = { workspace = true }
url = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive"] }
zeroize = { workspace = true }
nym-bin-common = { path = "../bin-common" }
nym-http-api-client = { path = "../http-api-client" }
reqwest = { workspace = true }
tracing = { workspace = true }
[dev-dependencies]
tokio = { workspace = true, features = ["full"] }
serde_json = { workspace = true }
[lints]
workspace = true
-151
View File
@@ -1,151 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
//! An incomplete Zulip API Client
//!
//! Currently, it serves a single purpose: to send a message to a server,
//! however, it could very easily be extended with additional functionalities.
//!
//! ## Sending Direct Message
//!
//! ```rust
//! # use zulip_client::{Client, ZulipClientError};
//! # use zulip_client::message::DirectMessage;
//! # async fn try_send() -> Result<(), ZulipClientError> {
//! let api_key = "your-api-key";
//! let email = "associated-email-address";
//! let server = "https://server-address.com";
//! let client = Client::builder(email, api_key, server)?.build()?;
//! // send to userid 12
//! client.send_message((12u32, "hello world")).await?;
//! // more concrete typing
//! client.send_message(DirectMessage::new(12, "hello world2")).await?;
//! # Ok(())
//! # }
//! ```
use crate::error::ZulipClientError;
use crate::message::{SendMessageResponse, SendableMessage};
use nym_bin_common::bin_info;
use nym_http_api_client::UserAgent;
use reqwest::{header, Method, RequestBuilder};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use tracing::trace;
use url::Url;
use zeroize::Zeroizing;
#[derive(Serialize, Deserialize)]
pub struct ClientConfig {
pub user_email: String,
pub api_key: String,
// TODO: introduce validation
pub user_agent: Option<String>,
pub server_url: Url,
}
pub struct Client {
server_url: Url,
api_key: Zeroizing<String>,
user_email: String,
inner_client: reqwest::Client,
}
fn default_user_agent() -> String {
UserAgent::from(bin_info!()).to_string()
}
impl Client {
const MESSAGES_ENDPOINT: &'static str = "/api/v1/messages";
pub fn builder(
user_email: impl Into<String>,
api_key: impl Into<String>,
server_url: impl Into<String>,
) -> Result<ClientBuilder, ZulipClientError> {
ClientBuilder::new(user_email, api_key, server_url)
}
pub fn new(config: ClientConfig) -> Result<Self, ZulipClientError> {
let builder = ClientBuilder::new(config.user_email, config.api_key, config.server_url)?;
match config.user_agent {
Some(user_agent) => builder.user_agent(user_agent).build(),
None => builder.build(),
}
}
pub async fn send_message(
&self,
msg: impl Into<SendableMessage>,
) -> Result<SendMessageResponse, ZulipClientError> {
let url = format!("{}{}", self.server_url, Self::MESSAGES_ENDPOINT);
self.build_request(Method::POST, Self::MESSAGES_ENDPOINT)
.form(&msg.into())
.send()
.await
.map_err(|source| ZulipClientError::RequestSendingFailure { source, url })?
.json()
.await
.map_err(|source| ZulipClientError::RequestDecodeFailure { source })
}
fn build_request(&self, method: Method, endpoint: &'static str) -> RequestBuilder {
let url = format!("{}{endpoint}", self.server_url);
trace!("posting to {url}");
self.inner_client
.request(method, url)
.basic_auth(&self.user_email, Some(self.api_key.to_string()))
.header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
}
}
pub struct ClientBuilder {
api_key: Zeroizing<String>,
user_email: String,
server_url: Url,
user_agent: Option<String>,
}
impl ClientBuilder {
pub fn new(
user_email: impl Into<String>,
api_key: impl Into<String>,
server_url: impl Into<String>,
) -> Result<Self, ZulipClientError> {
let server_url = server_url.into();
let parsed_url =
Url::from_str(&server_url).map_err(|source| ZulipClientError::MalformedServerUrl {
raw: server_url,
source,
})?;
Ok(ClientBuilder {
api_key: Zeroizing::new(api_key.into()),
user_email: user_email.into(),
server_url: parsed_url,
user_agent: None,
})
}
#[must_use]
pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
self.user_agent = Some(user_agent.into());
self
}
pub fn build(self) -> Result<Client, ZulipClientError> {
let user_agent = self.user_agent.unwrap_or_else(default_user_agent);
Ok(Client {
api_key: self.api_key,
server_url: self.server_url,
user_email: self.user_email,
inner_client: reqwest::ClientBuilder::new()
.user_agent(user_agent)
.build()
.map_err(|source| ZulipClientError::ClientBuildFailure { source })?,
})
}
}
-22
View File
@@ -1,22 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ZulipClientError {
#[error("failed to send request to {url}: {source}")]
RequestSendingFailure { url: String, source: reqwest::Error },
#[error("failed to decode received response: {source}")]
RequestDecodeFailure { source: reqwest::Error },
#[error("failed to build internal client: {source}")]
ClientBuildFailure { source: reqwest::Error },
#[error("provided url ({raw}) is malformed: {source}")]
MalformedServerUrl {
raw: String,
source: url::ParseError,
},
}
-11
View File
@@ -1,11 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
pub mod client;
pub mod error;
pub mod message;
pub type Id = u32;
pub use client::{Client, ClientBuilder};
pub use error::ZulipClientError;
-215
View File
@@ -1,215 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::message::to::{ToChannel, ToDirect};
use serde::{Deserialize, Serialize};
pub mod to;
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "result")]
#[serde(rename_all = "snake_case")]
pub enum SendMessageResponse {
Success {
id: i64,
automatic_new_visibility_policy: Option<i64>,
msg: String,
},
Error {
code: String,
msg: String,
stream: Option<String>,
},
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
pub enum SendableMessageContent {
// old name: 'private'
Direct {
// internally this is a list
to: String,
content: String,
},
// alternative name: 'channel'
Stream {
to: String,
topic: Option<String>,
content: String,
},
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct SendableMessage {
#[serde(flatten)]
content: SendableMessageContent,
/// For clients supporting local echo, the event queue ID for the client.
/// If passed, `local_id` is required. If the message is successfully sent,
/// the server will include `local_id` in the message event that the client with this `queue_id`
/// will receive notifying it of the new message via `GET /events`.
/// This lets the client know unambiguously that it should replace the locally echoed message,
/// rather than adding this new message
/// (which would be correct if the user had sent the new message from another device).
/// example: "fb67bf8a-c031-47cc-84cf-ed80accacda8"
queue_id: Option<String>,
/// For clients supporting local echo, a unique string-format identifier chosen freely by the client;
/// the server will pass it back to the client without inspecting it, as described in the `queue_id` description.
/// example: "100.01"
local_id: Option<String>,
/// Whether the message should be initially marked read by its sender.
/// If unspecified, the server uses a heuristic based on the client name.
read_by_sender: bool,
}
impl SendableMessage {
pub fn new(content: impl Into<SendableMessageContent>) -> Self {
SendableMessage {
content: content.into(),
queue_id: None,
local_id: None,
read_by_sender: false,
}
}
#[must_use]
pub fn with_queue(mut self, queue_id: impl Into<String>, local_id: impl Into<String>) -> Self {
self.queue_id = Some(queue_id.into());
self.local_id = Some(local_id.into());
self
}
#[must_use]
pub fn read_by_sender(mut self, read_by_sender: bool) -> Self {
self.read_by_sender = read_by_sender;
self
}
}
pub type PrivateMessage = DirectMessage;
pub struct DirectMessage {
to: String,
content: String,
}
impl DirectMessage {
pub fn new(to: impl Into<ToDirect>, content: impl Into<String>) -> Self {
DirectMessage {
to: to.into().to_string(),
content: content.into(),
}
}
}
pub type ChannelMessage = StreamMessage;
pub struct StreamMessage {
to: String,
topic: Option<String>,
content: String,
}
impl StreamMessage {
pub fn new(
to: impl Into<ToChannel>,
content: impl Into<String>,
topic: Option<String>,
) -> Self {
StreamMessage {
to: to.into().to_string(),
topic,
content: content.into(),
}
}
pub fn no_topic(to: impl Into<ToChannel>, content: impl Into<String>) -> Self {
Self::new(to, content, None)
}
#[must_use]
pub fn with_topic(mut self, topic: impl Into<String>) -> Self {
self.topic = Some(topic.into());
self
}
}
impl From<SendableMessageContent> for SendableMessage {
fn from(content: SendableMessageContent) -> Self {
SendableMessage::new(content)
}
}
impl From<DirectMessage> for SendableMessage {
fn from(msg: DirectMessage) -> Self {
SendableMessageContent::from(msg).into()
}
}
impl From<DirectMessage> for SendableMessageContent {
fn from(msg: DirectMessage) -> Self {
SendableMessageContent::Direct {
to: msg.to,
content: msg.content,
}
}
}
impl<T, S> From<(T, S)> for DirectMessage
where
T: Into<ToDirect>,
S: Into<String>,
{
fn from((to, content): (T, S)) -> Self {
DirectMessage::new(to, content)
}
}
impl<T, S> From<(T, S)> for SendableMessage
where
T: Into<ToDirect>,
S: Into<String>,
{
fn from((to, content): (T, S)) -> Self {
DirectMessage::new(to, content).into()
}
}
impl From<StreamMessage> for SendableMessage {
fn from(msg: StreamMessage) -> Self {
SendableMessageContent::from(msg).into()
}
}
impl From<StreamMessage> for SendableMessageContent {
fn from(msg: StreamMessage) -> Self {
SendableMessageContent::Stream {
to: msg.to,
topic: msg.topic,
content: msg.content,
}
}
}
impl<T, S> From<(T, S, Option<S>)> for StreamMessage
where
T: Into<ToChannel>,
S: Into<String>,
{
fn from((to, content, topic): (T, S, Option<S>)) -> Self {
StreamMessage::new(to, content, topic.map(Into::into))
}
}
impl<T, S> From<(T, S, Option<S>)> for SendableMessage
where
T: Into<ToChannel>,
S: Into<String>,
{
fn from(inner: (T, S, Option<S>)) -> Self {
StreamMessage::from(inner).into()
}
}
-128
View File
@@ -1,128 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::Id;
use itertools::Itertools;
use std::fmt::Display;
// from the docs:
// For channel messages, either the name or integer ID of the channel.
// For direct messages, either a list containing integer user IDs
// or a list containing string Zulip API email addresses.
pub enum ToDirect {
ByIds(Vec<Id>),
ByNames(Vec<String>),
}
impl Display for ToDirect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ToDirect::ByIds(ids) => write!(f, "[{}]", ids.iter().join(",")),
ToDirect::ByNames(names) => {
write!(f, "[{}]", names.join(","))
}
}
}
}
impl From<Vec<String>> for ToDirect {
fn from(names: Vec<String>) -> Self {
ToDirect::ByNames(names)
}
}
impl From<&[String]> for ToDirect {
fn from(names: &[String]) -> Self {
names.to_vec().into()
}
}
impl From<&[&str]> for ToDirect {
fn from(names: &[&str]) -> Self {
names
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.into()
}
}
impl<const N: usize> From<&[&str; N]> for ToDirect {
fn from(names: &[&str; N]) -> Self {
names.as_slice().into()
}
}
impl From<Vec<&str>> for ToDirect {
fn from(names: Vec<&str>) -> Self {
names.as_slice().into()
}
}
impl From<String> for ToDirect {
fn from(name: String) -> Self {
ToDirect::ByNames(vec![name])
}
}
impl From<&str> for ToDirect {
fn from(name: &str) -> Self {
name.to_string().into()
}
}
impl From<Id> for ToDirect {
fn from(id: Id) -> Self {
ToDirect::ByIds(vec![id])
}
}
impl From<&[Id]> for ToDirect {
fn from(ids: &[Id]) -> Self {
ids.to_vec().into()
}
}
impl<const N: usize> From<&[Id; N]> for ToDirect {
fn from(ids: &[Id; N]) -> Self {
ids.as_slice().into()
}
}
impl From<Vec<Id>> for ToDirect {
fn from(ids: Vec<Id>) -> Self {
ToDirect::ByIds(ids)
}
}
pub enum ToChannel {
ByName(String),
ById(Id),
}
impl Display for ToChannel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ToChannel::ByName(name) => name.fmt(f),
ToChannel::ById(id) => id.fmt(f),
}
}
}
impl From<String> for ToChannel {
fn from(name: String) -> Self {
ToChannel::ByName(name)
}
}
impl From<&str> for ToChannel {
fn from(name: &str) -> Self {
name.to_string().into()
}
}
impl From<Id> for ToChannel {
fn from(id: Id) -> Self {
ToChannel::ById(id)
}
}
@@ -7,32 +7,20 @@ net.ipv6.conf.all.forwarding = 1
net.ipv4.ip_forward = 1
IP forwarding configured successfully.
Creating Nym exit policy chain...
Chain NYM-EXIT already exists. Flushing it...
Chain NYM-EXIT already exists in ip6tables. Flushing it...
NYM-EXIT all opt -- in * out nymwg 0.0.0.0/0 -> 0.0.0.0/0
NYM-EXIT all opt in * out nymwg ::/0 -> ::/0
Creating chain NYM-EXIT...
Creating chain NYM-EXIT in ip6tables...
Linking NYM-EXIT to FORWARD chain...
Linking NYM-EXIT to IPv6 FORWARD chain...
Setting up NAT rules...
MASQUERADE all opt -- in * out ens3 0.0.0.0/0 -> 0.0.0.0/0
IPv4 NAT rule already exists.
MASQUERADE all opt in * out ens3 ::/0 -> ::/0
IPv6 NAT rule already exists.
ACCEPT all opt -- in nymwg out ens3 0.0.0.0/0 -> 0.0.0.0/0
ACCEPT all opt -- in ens3 out nymwg 0.0.0.0/0 -> 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT all opt in nymwg out ens3 ::/0 -> ::/0
ACCEPT all opt in ens3 out nymwg ::/0 -> ::/0 state RELATED,ESTABLISHED
Configuring DNS and ICMP rules...
ACCEPT icmp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 icmptype 8
ACCEPT icmp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 icmptype 0
ACCEPT icmpv6 opt in * out * ::/0 -> ::/0
ACCEPT udp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 udp dpt:53
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpt:53
ACCEPT udp opt in * out * ::/0 -> ::/0 udp dpt:53
ACCEPT tcp opt in * out * ::/0 -> ::/0 tcp dpt:53
Added IPv6 ICMP rule (allow ping6).
Added IPv6 DNS rule (UDP).
Added IPv6 DNS rule (TCP).
Applying Spamhaus blocklist...
Downloading exit policy from https://nymtech.net/.wellknown/network-requester/exit-policy.txt
Processing 429 blocklist rules...
REJECT all opt -- in * out * 0.0.0.0/0 -> 205.189.71.0/24 reject-with icmp-port-unreachable
REJECT all opt -- in * out * 0.0.0.0/0 -> 205.189.72.0/23 reject-with icmp-port-unreachable
Blocklist applied successfully.
Applying allowed ports...
Adding rules for SILC (Port: 706)
@@ -115,11 +103,6 @@ Adding rules for POP3OverTLS (Port: 995)
Added: NYM-EXIT tcp port 995
Added: NYM-EXIT udp port 995
Added: NYM-EXIT udp port 995
Adding rules for DarkFiTor (Port: 25551)
Added: NYM-EXIT tcp port 25551
Added: NYM-EXIT tcp port 25551
Added: NYM-EXIT udp port 25551
Added: NYM-EXIT udp port 25551
Adding rules for MMCC (Port: 5050)
Added: NYM-EXIT tcp port 5050
Added: NYM-EXIT tcp port 5050
@@ -285,11 +268,6 @@ Adding rules for Mumble (Port: 64738)
Added: NYM-EXIT tcp port 64738
Added: NYM-EXIT udp port 64738
Added: NYM-EXIT udp port 64738
Adding rules for DarkFi (Port: 26661)
Added: NYM-EXIT tcp port 26661
Added: NYM-EXIT tcp port 26661
Added: NYM-EXIT udp port 26661
Added: NYM-EXIT udp port 26661
Adding rules for PPTP (Port: 1723)
Added: NYM-EXIT tcp port 1723
Added: NYM-EXIT tcp port 1723
@@ -355,11 +333,6 @@ Adding rules for Kpasswd (Port: 464)
Added: NYM-EXIT tcp port 464
Added: NYM-EXIT udp port 464
Added: NYM-EXIT udp port 464
Adding rules for MoneroRPC (Port: 18089)
Added: NYM-EXIT tcp port 18089
Added: NYM-EXIT tcp port 18089
Added: NYM-EXIT udp port 18089
Added: NYM-EXIT udp port 18089
Adding rules for RemoteHTTPS (Port: 981)
Added: NYM-EXIT tcp port 981
Added: NYM-EXIT tcp port 981
@@ -425,11 +398,6 @@ Adding rules for GroupWise (Port: 1677)
Added: NYM-EXIT tcp port 1677
Added: NYM-EXIT udp port 1677
Added: NYM-EXIT udp port 1677
Adding rules for Monero (Port: 18080-18081)
Added: NYM-EXIT tcp port range 18080:18081
Added: NYM-EXIT tcp port range 18080:18081
Added: NYM-EXIT udp port range 18080:18081
Added: NYM-EXIT udp port range 18080:18081
Adding rules for EnsimControlPanel (Port: 19638)
Added: NYM-EXIT tcp port 19638
Added: NYM-EXIT tcp port 19638
File diff suppressed because it is too large Load Diff
@@ -2,61 +2,41 @@
Running Nym Exit Policy Verification Tests...
Testing Port Range Rules...
Testing FTP tcp port range 20-21
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:20:21
✓ Rule exists: NYM-EXIT tcp port range 20:21
Testing HTTP tcp port range 80-81
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:80:81
✓ Rule exists: NYM-EXIT tcp port range 80:81
Testing CPanel tcp port range 2082-2083
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:2082:2083
✓ Rule exists: NYM-EXIT tcp port range 2082:2083
Testing XMPP tcp port range 5222-5223
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:5222:5223
✓ Rule exists: NYM-EXIT tcp port range 5222:5223
Testing Steam (sampling) tcp port range 27000-27050
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:27000:27050
✓ Rule exists: NYM-EXIT tcp port range 27000:27050
Testing FTP over TLS tcp port range 989-990
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:989:990
✓ Rule exists: NYM-EXIT tcp port range 989:990
Testing RTP/VoIP tcp port range 5000-5005
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:5000:5005
✓ Rule exists: NYM-EXIT tcp port range 5000:5005
Testing Simplify Media tcp port range 8087-8088
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:8087:8088
✓ Rule exists: NYM-EXIT tcp port range 8087:8088
Testing Zcash tcp port range 8232-8233
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:8232:8233
✓ Rule exists: NYM-EXIT tcp port range 8232:8233
Testing Bitcoin tcp port range 8332-8333
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:8332:8333
✓ Rule exists: NYM-EXIT tcp port range 8332:8333
Testing Monero tcp port range 18080-18081
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpts:18080:18081
✓ Rule exists: NYM-EXIT tcp port range 18080:18081
Test test_port_range_rules PASSED
Testing Critical Service Rules...
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpt:22
✓ Rule exists: NYM-EXIT tcp port 22
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpt:53
✓ Rule exists: NYM-EXIT tcp port 53
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpt:443
✓ Rule exists: NYM-EXIT tcp port 443
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpt:853
✓ Rule exists: NYM-EXIT tcp port 853
ACCEPT tcp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 tcp dpt:1194
✓ Rule exists: NYM-EXIT tcp port 1194
ACCEPT udp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 udp dpt:53
✓ Rule exists: NYM-EXIT udp port 53
ACCEPT udp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 udp dpt:123
✓ Rule exists: NYM-EXIT udp port 123
ACCEPT udp opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0 udp dpt:1194
✓ Rule exists: NYM-EXIT udp port 1194
Relevant existing rules for HTTP (port 80):
Test test_critical_services PASSED
This test takes some time, do not quit the process
Testing Default Reject Rule...
✓ Default REJECT rule exists
Test test_default_reject_rule PASSED
@@ -1 +1 @@
Wednesday, July 30th 2025, 09:32:50 UTC
Tuesday, July 22nd 2025, 11:37:27 UTC
@@ -1,44 +1,21 @@
| **ISP** | **Locations** | **Public IPv6** | **Crypto Payments** | **Comments** | **Last Updated** |
|:---------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------|:---------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------|
| [AlexHost](https://alexhost.com) | Moldova, Bulgaria, Sweden, Netherlands | Yes, on by default | Yes | They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR) | 07/2024 |
| [AmeriNoc](https://www.amerinoc.com) | USA | Yes | nan | nan | 07/2025 |
| [BitLaunch](https://bitlaunch.io) | Canada, USA, UK | No | Yes | Expensive. Digial Ocean through BitLanch has IPv6 | 05/2024 |
| [Cherry Servers](https://www.cherryservers.com) | Lithuania, Netherlands, USA, Singapore | No | Yes | Issued IP doesnt match the location offered by the provider. | 05/2024 |
| [Colocall](https://www.colocall.net/) | Ukraine | Yes | nan | 07/2025 | nan |
| [DataPacket](https://www.datapacket.com/pricing) | NL, GR, SK, BE, RO, HU, DK, IE, DE, UA, PT, GB, ES, FR, IT, NO, CZ, BG, SE, AT, PL, HR, CH, USA, CO, AR, PE, MX, CL, TR, ZA, NG, IL, HK, AU, SG, JP | Yes | nan | nan | 07/2025 |
| [Dataclub](https://www.dataclub.eu/) | Latvia, Sweden, Netherlands | Yes | nan | nan | 07/2027 |
| [Flokinet](https://flokinet.is) | Netherlands, Iceland, Romania,France | Yes, needs a ticket and custom setup | yes, including XMR | Very slow customer support | 05/2024 |
| [FranTech](https://my.frantech.ca) | USA | Yes | nan | nan | 07/2025 |
| [Fsit](https://www.fsit.com/server/vps-vserver-kvm) | Swiss | Yes | Yes | nan | 07/2025 |
| [HostSailor](https://hostsailor.com) | USA | Yes, based on ticket | Yes | The IPv6 setup needs custom research and is not documented | 05/2024 |
| [Hostiko](https://hostiko.com.ua) | Ukraine, Germany | Yes, on by default | Yes | Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket. | 07/2024 |
| [Hostinger](https://hostinger.com) | France, Lithuania, India, USA, Brazil | Yes, out of the box | Yes | Not fast enough, Crypto payments must be done per each server monthly or annually. | 07/2025 |
| [Hostroyale](https://hostroyale.com/hosting/dedicated-server/) | Various countries with different pricing | nan | Yes | nan | 07/2025 |
| [Hostslick](https://hostslick.com) | Netherlands, Germany | Yes, on by default | Yes | Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node | 07/2024 |
| [Incognet](https://incognet.io) | Netherlands and USA | Yes, on by default | Yes | They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits | 07/2024 |
| [Incognet](https://incognet.io/kansas-city-dedicated-servers) | USA, Netherlands | Yes | nan | nan | 07/2025 |
| [Ionos](https://www.ionos.com/servers/amd-servers) | US, DE, UK, ESP, FR | nan | No | nan | 07/2025 |
| [IsHosting](https://ishosting.com/en) | Brazil, Netherlands | Yes, based on ticket | Yes | Expensive | 05/2024 |
| [Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134) | US, NL, DE, UK, CA, SG, JP, AUS, HK | nan | No | KYC mandatory | 07/2025 |
| [Linode](https://linode.com) | USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy | Yes out of the box | No, only through [BitLAunch](https://bitlaunch.io) | IPv6 sometimes need to be re-added in Networking tab, no reboot needed | 05/2024 |
| [LiteServer](https://liteserver.nl) | Netherlands | Yes, on by default | Yes | Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal | 07/2024 |
| [Lowendbox](https://lowendbox.com/category/dedicated-servers) | | | | Just an aggregator with good offers | 07/2025 |
| [M247](https://m247.com/eu/services/host/dedicated-servers/) | UK, Austria, Br, Sw, Jp, Poland, Fr, USA, Netherlands | Yes | No | nan | 07/2025 |
| [Mebilcom](https://www.melbicom.net/dedicatedserver/) | NL, US, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL | nan | No | nan | 07/2025 |
| [Mevspace](https://mevspace.com) | Poland | Yes, on by default | Yes | Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff | 07/2024 |
| [Misaka](https://www.misaka.io/) | South Africa | Yes, native support | No | Very Expensive | 05/2024 |
| [NiceVPS](https://nicevps.net/) | Netherlands | Yes | nan | nan | 07/2025 |
| [Njalla](https://nja.la) | Sweden | Yes | Yes | Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market. | 05/2024 |
| [OVH](https://us.ovhcloud.com/bare-metal/rise/rise-3/) | USA, DE, FR, UK, PL, CA | | No | Not all locations always available | 07/2025 |
| [Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6) | PL, FR, NL, UA, US, BG, RO, DK, ESP, NO, CZ, RS, IE, IT, UK, HU, CH, SK, AT, BE, BA, HK, JP, SG, LU, AU, SWE, UAE, BR, CR, MX, GR, CL, MA, AR | Yes | No | nan | 07/2025 |
| [PrivateLayer](https://privatelayer.com) | Swiss | Yes | Yes | Slow customer response | 07/2025 |
| [Privex](https://www.privex.io/tor-exit-policy/) | USA, Germany, Sweden | Yes | Yes | nan | 07/2025 |
| [Psychz](https://www.psychz.net) | US, UK, Brazil, Japan, Russia, South Africa and many more | Yes | nan | nan | 07/2025 |
| [RDP](https://rdp.sh) | Netherlands, USA, Poland | Yes, on by default | Yes | German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node. | 07/2024 |
| [Servermania](https://www.servermania.com/dedicated-servers-hosting.htm) | USA, Canada | nan | No | nan | 07/2025 |
| [Svea](https://svea.net/vps) | Sweden | Yes | nan | nan | 07/2025 |
| [TerraHost](https://terrahost.no) | Norway | Yes, on by default | Yes | Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser | 07/2024 |
| [Thundervm](https://thundervm.com/en/hosting/dedicated-server) | USA, UK, France, Italy, Switzerland, Netherlands | nan | Yes | | 07/2025 |
| [Zenlayer](https://www.zenlayer.com/bare-metal/) | [advertised over 50 locations](50+ https://www.zenlayer.com/global-network) | nan | nan | nan | 07/2025 |
| [iHostArt](https://ihostart.com) | Romania | Yes, on by default | Yes | Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware. | 07/2024 |
| [vSys Host](https://vsys.host) | Ukraine, Netherlands, USA | Yes, on by default | Yes | Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check. | 07/2024 |
| **ISP** | **Locations** | **Public IPv6** | **Crypto Payments** | **Comments** | **Last Updated** |
|:------------------------------------------------|:---------------------------------------------------------------------------------------------------------|:-------------------------------------|:---------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------|
| [AlexHost](https://alexhost.com) | Moldova, Bulgaria, Sweden, Netherlands | Yes, on by default | Yes | They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR) | 07/2024 |
| [BitLaunch](https://bitlaunch.io) | Canada, USA, UK | No | Yes | Expensive. Digial Ocean through BitLanch has IPv6 | 05/2024 |
| [Cherry Servers](https://www.cherryservers.com) | Lithuania, Netherlands, USA, Singapore | No | Yes | Issued IP doesnt match the location offered by the provider. | 05/2024 |
| [Flokinet](https://flokinet.is) | Netherlands, Iceland, Romania,France | Yes, needs a ticket and custom setup | yes, including XMR | Very slow customer support | 05/2024 |
| [HostSailor](https://hostsailor.com) | USA | Yes, based on ticket | Yes | The IPv6 setup needs custom research and is not documented | 05/2024 |
| [Hostiko](https://hostiko.com.ua) | Ukraine, Germany | Yes, on by default | Yes | Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket. | 07/2024 |
| [Hostinger](https://hostinger.com) | France, Lithuania, India, USA, Brazil | Yes, out of the box | Yes | Crypto payments must be done per each server monthly or annually. | 05/2024 |
| [Hostslick](https://hostslick.com) | Netherlands, Germany | Yes, on by default | Yes | Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node | 07/2024 |
| [Incognet](https://incognet.io) | Netherlands and USA | Yes, on by default | Yes | They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits | 07/2024 |
| [IsHosting](https://ishosting.com/en) | Brazil, Netherlands | Yes, based on ticket | Yes | Expensive | 05/2024 |
| [Linode](https://linode.com) | USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy | Yes out of the box | No, only through [BitLAunch](https://bitlaunch.io) | IPv6 sometimes need to be re-added in Networking tab, no reboot needed | 05/2024 |
| [LiteServer](https://liteserver.nl) | Netherlands | Yes, on by default | Yes | Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal | 07/2024 |
| [Mevspace](https://mevspace.com) | Poland | Yes, on by default | Yes | Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff | 07/2024 |
| [Misaka](https://www.misaka.io/) | South Africa | Yes, native support | No | Very Expensive | 05/2024 |
| [Njalla](https://nja.la) | Sweden | Yes | Yes | Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market. | 05/2024 |
| [RDP](https://rdp.sh) | Netherlands, USA, Poland | Yes, on by default | Yes | German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node. | 07/2024 |
| [TerraHost](https://terrahost.no) | Norway | Yes, on by default | Yes | Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser | 07/2024 |
| [iHostArt](https://ihostart.com) | Romania | Yes, on by default | Yes | Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware. | 07/2024 |
| [vSys Host](https://vsys.host) | Ukraine, Netherlands, USA | Yes, on by default | Yes | Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check. | 07/2024 |
+25 -42
View File
@@ -1,43 +1,26 @@
**ISP**,**Locations**,**Public IPv6**,**Crypto Payments**,**Comments**,**Last Updated**
[Flokinet](https://flokinet.is),"Netherlands, Iceland, Romania,France","Yes, needs a ticket and custom setup","yes, including XMR",Very slow customer support,05/2024
[BitLaunch](https://bitlaunch.io),"Canada, USA, UK",No,Yes,Expensive. Digial Ocean through BitLanch has IPv6,05/2024
[Hostinger](https://hostinger.com),"France, Lithuania, India, USA, Brazil","Yes, out of the box",Yes,"Not fast enough, Crypto payments must be done per each server monthly or annually.",07/2025
[Linode](https://linode.com),"USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy",Yes out of the box,"No, only through [BitLAunch](https://bitlaunch.io)","IPv6 sometimes need to be re-added in Networking tab, no reboot needed",05/2024
[Cherry Servers](https://www.cherryservers.com),"Lithuania, Netherlands, USA, Singapore",No,Yes,Issued IP doesnt match the location offered by the provider.,05/2024
[Njalla](https://nja.la),Sweden,Yes,Yes,"Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market.",05/2024
[HostSailor](https://hostsailor.com),USA,"Yes, based on ticket",Yes,The IPv6 setup needs custom research and is not documented,05/2024
[Misaka](https://www.misaka.io/),South Africa,"Yes, native support",No,Very Expensive,05/2024
[IsHosting](https://ishosting.com/en),"Brazil, Netherlands","Yes, based on ticket",Yes,Expensive,05/2024
[AlexHost](https://alexhost.com),"Moldova, Bulgaria, Sweden, Netherlands","Yes, on by default",Yes,"They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR)",07/2024
[iHostArt](https://ihostart.com),Romania,"Yes, on by default",Yes,"Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware.",07/2024
[Incognet](https://incognet.io),Netherlands and USA,"Yes, on by default",Yes,They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits,07/2024
[vSys Host](https://vsys.host),"Ukraine, Netherlands, USA","Yes, on by default",Yes,"Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check.",07/2024
[LiteServer](https://liteserver.nl),Netherlands,"Yes, on by default",Yes,Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal,07/2024
[TerraHost](https://terrahost.no),Norway,"Yes, on by default",Yes,Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser,07/2024
[Mevspace](https://mevspace.com),Poland,"Yes, on by default",Yes,"Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff",07/2024
[Hostiko](https://hostiko.com.ua),"Ukraine, Germany","Yes, on by default",Yes,"Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket.",07/2024
[Hostslick](https://hostslick.com),"Netherlands, Germany","Yes, on by default",Yes,Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node,07/2024
[RDP](https://rdp.sh),"Netherlands, USA, Poland","Yes, on by default",Yes,"German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node.",07/2024
[Lowendbox](https://lowendbox.com/category/dedicated-servers), , , ,Just an aggregator with good offers,07/2025
[Thundervm](https://thundervm.com/en/hosting/dedicated-server),"USA, UK, France, Italy, Switzerland, Netherlands",,Yes, ,07/2025
[OVH](https://us.ovhcloud.com/bare-metal/rise/rise-3/),"USA, DE, FR, UK, PL, CA", ,No,Not all locations always available,07/2025
[Mebilcom](https://www.melbicom.net/dedicatedserver/),"NL, US, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL",,No,,07/2025
[Servermania](https://www.servermania.com/dedicated-servers-hosting.htm),"USA, Canada",,No,,07/2025
[Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6),"PL, FR, NL, UA, US, BG, RO, DK, ESP, NO, CZ, RS, IE, IT, UK, HU, CH, SK, AT, BE, BA, HK, JP, SG, LU, AU, SWE, UAE, BR, CR, MX, GR, CL, MA, AR",Yes,No,,07/2025
[Ionos](https://www.ionos.com/servers/amd-servers),"US, DE, UK, ESP, FR",,No,,07/2025
[Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134),"US, NL, DE, UK, CA, SG, JP, AUS, HK",,No,KYC mandatory,07/2025
[M247](https://m247.com/eu/services/host/dedicated-servers/),"UK, Austria, Br, Sw, Jp, Poland, Fr, USA, Netherlands",Yes,No,,07/2025
[Hostroyale](https://hostroyale.com/hosting/dedicated-server/),Various countries with different pricing,, Yes,,07/2025
[DataPacket](https://www.datapacket.com/pricing),"NL, GR, SK, BE, RO, HU, DK, IE, DE, UA, PT, GB, ES, FR, IT, NO, CZ, BG, SE, AT, PL, HR, CH, USA, CO, AR, PE, MX, CL, TR, ZA, NG, IL, HK, AU, SG, JP",Yes,,,07/2025
[Zenlayer](https://www.zenlayer.com/bare-metal/), [advertised over 50 locations](50+ https://www.zenlayer.com/global-network),,,,07/2025
[PrivateLayer](https://privatelayer.com),Swiss,Yes,Yes,Slow customer response,07/2025
[AmeriNoc](https://www.amerinoc.com),USA,Yes,,,07/2025
[Colocall](https://www.colocall.net/),Ukraine,Yes,,07/2025,
[Incognet](https://incognet.io/kansas-city-dedicated-servers),"USA, Netherlands",Yes,,,07/2025
[FranTech](https://my.frantech.ca),USA,Yes,,,07/2025
[Psychz](https://www.psychz.net),"US, UK, Brazil, Japan, Russia, South Africa and many more",Yes,,,07/2025
[Fsit](https://www.fsit.com/server/vps-vserver-kvm),Swiss,Yes,Yes,,07/2025
[NiceVPS](https://nicevps.net/),Netherlands,Yes,,,07/2025
[Dataclub](https://www.dataclub.eu/),"Latvia, Sweden, Netherlands",Yes,,,07/2027
[Privex](https://www.privex.io/tor-exit-policy/),"USA, Germany, Sweden",Yes,Yes,,07/2025
[Svea](https://svea.net/vps),Sweden,Yes,,,07/2025
[Flokinet](https://flokinet.is),"Netherlands, Iceland, Romania,France","Yes, needs a ticket and custom setup","yes, including XMR","Very slow customer support","05/2024"
[BitLaunch](https://bitlaunch.io),"Canada, USA, UK","No","Yes","Expensive. Digial Ocean through BitLanch has IPv6","05/2024"
[Hostinger](https://hostinger.com),"France, Lithuania, India, USA, Brazil","Yes, out of the box","Yes","Crypto payments must be done per each server monthly or annually.","05/2024"
[Linode](https://linode.com),"USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy","Yes out of the box","No, only through [BitLAunch](https://bitlaunch.io)","IPv6 sometimes need to be re-added in Networking tab, no reboot needed","05/2024"
[Cherry Servers](https://www.cherryservers.com),"Lithuania, Netherlands, USA, Singapore","No","Yes","Issued IP doesnt match the location offered by the provider.","05/2024"
[Njalla](https://nja.la),"Sweden","Yes","Yes","Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market.","05/2024"
[HostSailor](https://hostsailor.com),"USA","Yes, based on ticket","Yes","The IPv6 setup needs custom research and is not documented","05/2024"
[Misaka](https://www.misaka.io/),"South Africa","Yes, native support","No","Very Expensive","05/2024"
[IsHosting](https://ishosting.com/en),"Brazil, Netherlands","Yes, based on ticket","Yes","Expensive","05/2024"
[AlexHost](https://alexhost.com),"Moldova, Bulgaria, Sweden, Netherlands","Yes, on by default","Yes","They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR)","07/2024"
[iHostArt](https://ihostart.com),"Romania","Yes, on by default","Yes","Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware.","07/2024"
[Incognet](https://incognet.io),"Netherlands and USA","Yes, on by default","Yes","They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits","07/2024"
[vSys Host](https://vsys.host),"Ukraine, Netherlands, USA","Yes, on by default","Yes","Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check.","07/2024"
[LiteServer](https://liteserver.nl),"Netherlands","Yes, on by default","Yes","Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal","07/2024"
[TerraHost](https://terrahost.no),"Norway","Yes, on by default","Yes","Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser","07/2024"
[Mevspace](https://mevspace.com),"Poland","Yes, on by default","Yes","Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff","07/2024"
[Hostiko](https://hostiko.com.ua),"Ukraine, Germany","Yes, on by default","Yes","Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket.","07/2024"
[Hostslick](https://hostslick.com),"Netherlands, Germany","Yes, on by default","Yes","Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node","07/2024"
[RDP](https://rdp.sh),"Netherlands, USA, Poland","Yes, on by default","Yes","German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node.","07/2024"
1 **ISP** **Locations** **Public IPv6** **Crypto Payments** **Comments** **Last Updated**
2 [Flokinet](https://flokinet.is) Netherlands, Iceland, Romania,France Yes, needs a ticket and custom setup yes, including XMR Very slow customer support 05/2024
3 [BitLaunch](https://bitlaunch.io) Canada, USA, UK No Yes Expensive. Digial Ocean through BitLanch has IPv6 05/2024
4 [Hostinger](https://hostinger.com) France, Lithuania, India, USA, Brazil Yes, out of the box Yes Not fast enough, Crypto payments must be done per each server monthly or annually. Crypto payments must be done per each server monthly or annually. 07/2025 05/2024
5 [Linode](https://linode.com) USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy Yes out of the box No, only through [BitLAunch](https://bitlaunch.io) IPv6 sometimes need to be re-added in Networking tab, no reboot needed 05/2024
6 [Cherry Servers](https://www.cherryservers.com) Lithuania, Netherlands, USA, Singapore No Yes Issued IP doesn’t match the location offered by the provider. 05/2024
7 [Njalla](https://nja.la) Sweden Yes Yes Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market. 05/2024
8 [HostSailor](https://hostsailor.com) USA Yes, based on ticket Yes The IPv6 setup needs custom research and is not documented 05/2024
9 [Misaka](https://www.misaka.io/) South Africa Yes, native support No Very Expensive 05/2024
10 [IsHosting](https://ishosting.com/en) Brazil, Netherlands Yes, based on ticket Yes Expensive 05/2024
11 [AlexHost](https://alexhost.com) Moldova, Bulgaria, Sweden, Netherlands Yes, on by default Yes They allow TOR Bridges, Relays. Exit nodes are only allowed on dedicated servers (prices start from 26 EUR) 07/2024
12 [iHostArt](https://ihostart.com) Romania Yes, on by default Yes Super permissive provider. They do allow Tor Exit/Relay/Bridge. Pro-free speech etc. Recently, IPv6 geolocation was set to North Korea, so be aware. 07/2024
13 [Incognet](https://incognet.io) Netherlands and USA Yes, on by default Yes They allow Tor exit nodes but you must adhere to their rules https://incognet.io/tor-exits 07/2024
14 [vSys Host](https://vsys.host) Ukraine, Netherlands, USA Yes, on by default Yes Pretty permissive provider registered in Ukraine. Should allow Relay/Exit nodes but nothing in T&C, so better double check. 07/2024
15 [LiteServer](https://liteserver.nl) Netherlands Yes, on by default Yes Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal 07/2024
16 [TerraHost](https://terrahost.no) Norway Yes, on by default Yes Very reliable Norwegian provider. Only allow exit nodes on Dedicated servers subject to certain caveats (you must open a ticket). Always check T&C https://terrahost.no/avtalebetingelser 07/2024
17 [Mevspace](https://mevspace.com) Poland Yes, on by default Yes Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff 07/2024
18 [Hostiko](https://hostiko.com.ua) Ukraine, Germany Yes, on by default Yes Ukrainian provider. They allow Exit nodes on Germany boxes but limit the bandwidth, you also have to restrict certain ports like 25 and 587. Make sure you open a ticket. 07/2024
19 [Hostslick](https://hostslick.com) Netherlands, Germany Yes, on by default Yes Good amount of bandwidth for the price. Make sure you open the ticket if you want to run Exit node 07/2024
20 [RDP](https://rdp.sh) Netherlands, USA, Poland Yes, on by default Yes German provider. Exit nodes are allowed, policy is here https://rdp.sh/docs/faq/tor ports 25,465,587 must be closed. Make sure you open a ticket before running an exit node. 07/2024
21
22
23
24
25
26
[Ionos](https://www.ionos.com/servers/amd-servers) US, DE, UK, ESP, FR No 07/2025
[Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134) US, NL, DE, UK, CA, SG, JP, AUS, HK No KYC mandatory 07/2025
[M247](https://m247.com/eu/services/host/dedicated-servers/) UK, Austria, Br, Sw, Jp, Poland, Fr, USA, Netherlands Yes No 07/2025
[Hostroyale](https://hostroyale.com/hosting/dedicated-server/) Various countries with different pricing Yes 07/2025
[DataPacket](https://www.datapacket.com/pricing) NL, GR, SK, BE, RO, HU, DK, IE, DE, UA, PT, GB, ES, FR, IT, NO, CZ, BG, SE, AT, PL, HR, CH, USA, CO, AR, PE, MX, CL, TR, ZA, NG, IL, HK, AU, SG, JP Yes 07/2025
[Zenlayer](https://www.zenlayer.com/bare-metal/) [advertised over 50 locations](50+ https://www.zenlayer.com/global-network) 07/2025
[PrivateLayer](https://privatelayer.com) Swiss Yes Yes Slow customer response 07/2025
[AmeriNoc](https://www.amerinoc.com) USA Yes 07/2025
[Colocall](https://www.colocall.net/) Ukraine Yes 07/2025
[Incognet](https://incognet.io/kansas-city-dedicated-servers) USA, Netherlands Yes 07/2025
[FranTech](https://my.frantech.ca) USA Yes 07/2025
[Psychz](https://www.psychz.net) US, UK, Brazil, Japan, Russia, South Africa and many more Yes 07/2025
[Fsit](https://www.fsit.com/server/vps-vserver-kvm) Swiss Yes Yes 07/2025
[NiceVPS](https://nicevps.net/) Netherlands Yes 07/2025
[Dataclub](https://www.dataclub.eu/) Latvia, Sweden, Netherlands Yes 07/2027
[Privex](https://www.privex.io/tor-exit-policy/) USA, Germany, Sweden Yes Yes 07/2025
[Svea](https://svea.net/vps) Sweden Yes 07/2025
@@ -24,23 +24,25 @@ Write a message to your provider telling them about your intention to run a `nym
</AccordionTemplate>
#### Join Operators Legal Forum
This [Matrix channel]((https://matrix.to/#/!YfoUFsJjsXbWmijbPG:nymtech.chat?via=nymtech.chat&via=matrix.org&via=matrix.su4ka.icu)) is the best place to ask questions and share your experience with others. You can share screen prints of abuse reports and ask for support.
This [Matrix channel]((https://matrix.to/#/!YfoUFsJjsXbWmijbPG:nymtech.chat?via=nymtech.chat&via=matrix.org&via=matrix.su4ka.icu)) is the best place to ask questions and share your experience with others. You can share screen prints of abuse reports and ask for support.
#### Join Operators Legal Clinic
Do you have any questions directed for lawyers? Come and chat with Nym COO Alexis Roussel, every Wednesday 14:30 UTC for 60min in our [Operator Legal Forum channel on Matrix](https://matrix.to/#/!YfoUFsJjsXbWmijbPG:nymtech.chat?via=nymtech.chat&via=matrix.org&via=matrix.su4ka.icu).
#### Use a friendly provider
Nym operators community shares their experience with different ISPs on [this page](isp-list). At the same time, consider to move away from these provides:
- Servinga / VPS2day (AS39378)
- Frantech / Ponynet / BuyVM (AS53667)
- OVH SAS / OVHcloud (AS16276)
- Online S.A.S. / Scaleway (AS12876)
- Hetzner Online GmbH (AS24940)
- IONOS SE (AS8560)
- Psychz Networks (AS40676)
- 1337 Services GmbH / RDP.sh (AS210558)
- Stark Industries Solutions Ltd. / PQ.Hosting / The.Hosting / UFO-AS (AS44477 / ASN 33993)
#### Backup your nodes
Your only way to restore your node is when you have an access to `.nym` directory locally. Follow [node](../nodes/maintenance#backup-a-node) and [proxy configuration](../nodes/maintenance#backup-proxy-configuration) backup guides to be able to [restore your node](../nodes/maintenance#restoring-a-node) on another machine later on, without losing your delegation.
Your only way to restore your node is when you have an access to `.nym` directory locally. Follow [node](../nodes/maintenance#backup-a-node) and [proxy configuration](../nodes/maintenance#backup-proxy-configuration) backup guides to be able to [restore your node](../nodes/maintenance#restoring-a-node) on another machine later on, without losing your delegation.
#### Use `nym-exit` prefix on your landing page URL
We would like to ask operators to use [reverse proxy](../nodes/nym-node/configuration/proxy-configuration) with a [landing page](landing-pages). When assigning a domain please use a common convention with `nym-exit` in the beginning of the the page URL as this will create a reputation and reference. The entire address should have this new format:
@@ -67,10 +69,10 @@ nym-exit.mysquad.org
**The `NYM-EXIT` part in the beginning is what's important.**
#### Chose the right TLD
When registering a domain, check [Top Level Domain (TLD)](https://www.techopedia.com/definition/1348/top-level-domain-tld) terms and conditions. For example `.icu` is a no go. Having a wrong TLD may lead to your domain being taken away from you when facing a DMCA report.
When registering a domain, check [Top Level Domain (TLD)](https://www.techopedia.com/definition/1348/top-level-domain-tld) terms and conditions. For example `.icu` is a no go. Having a wrong TLD may lead to your domain being taken away from you when facing a DMCA report.
#### Respond to abuse reports
Make sure to read notifications from your account provider and if you receive an abuse report, respond to it in time. Here is a template explaining the essential legal background of Nym Node Exit Gateway. Don't forget to adjust the variables.
Make sure to read notifications from your account provider and if you receive an abuse report, respond to it in time. Here is a template explaining the essential legal background of Nym Node Exit Gateway. Don't forget to adjust the variables.
<br/>
<AccordionTemplate name="Email template: responding to DMCA take-down notices">
@@ -79,3 +81,4 @@ Make sure to read notifications from your account provider and if you receive an
#### Help us to improve these pages
Add your findings by opening a [Pull Request](add-content).
@@ -454,7 +454,7 @@ The exit policy is same for all NRs, the content is shaped by an offchain govern
There is a caveat though. NR is only routing TCP streams and therefore any other type of routing is *not* filtered thorugh the exit policy. To ensure that Nym Nodes follow the same exit policy when routing IP packets through wireguard and don't act as open proxies, the operators have to set up these rules via IP tables rules.
**Follow these steps, using a [setup script](https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh) and [testing scripts](https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/exit-policy-tests.sh) written by Nym quality assurance team, to setup exit policy for wireguard:**
**Follow these steps, using a [setup script]i(https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh) and [testing scripts](https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/exit-policy-tests.sh) written by Nym quality assurance team, to setup exit policy for wireguard:**
<Steps>
@@ -9,12 +9,12 @@ import { AccordionTemplate } from 'components/accordion-template.tsx';
> Nym has two main codebases:
> - the [Nym platform](https://github.com/nymtech/nym), written in Rust. This contains all of our code except for the validators.
> - the [Nym validators](https://github.com/nymtech/nyxd), written in Go & maintained as fork of [wasmd](https://github.com/CosmWasm/wasmd)
> - the [Nym validators](https://github.com/nymtech/nyxd), written in Go & maintained as a no-modification fork of [wasmd](https://github.com/CosmWasm/wasmd)
The validator is a Go application which implements it's functionalities using [Cosmos SDK](https://v1.cosmos.network/sdk). The underlying state-replication engine is powered by [CometBFT](https://cometbft.com/), where the consensus mechanism is based on the [Tendermint Consensus Algorithm](https://arxiv.org/abs/1807.04938). Finally, a [CosmWasm](https://cosmwasm.com) smart contract module controls crucial mixnet functionalities like decentralised directory service, node bonding, and delegated mixnet staking.
<Callout type="info" emoji="️">
At present, our mainnet operates with a select group of reputed validators. We are not accepting new validators at this time. Any updates or changes to this policy will be promptly announced.
We are currently running mainnet with a closed set of reputable validators. To ensure decentralisation of Nyx chain, we are working on a mechanism to onboard new validators to the network. To join the waitlist, please drop an email to `validators [at] nymtech.net` with details of your setup, experience and any other relevent information
</Callout>
## Building your validator
@@ -52,7 +52,7 @@ pacman -S git gcc jq
- First remove any existing old Go installation and extract the archive you just downloaded into /usr/local:
```sh
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.23.11.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.10.linux-amd64.tar.gz
```
- Then add /usr/local/go/bin to the PATH environment variable
@@ -69,14 +69,14 @@ go version
- Should return something like:
```sh
go version go1.23.11 linux/amd64
go version go1.20.10 linux/amd64
```
</Steps>
### Download a precompiled validator binary
You can find pre-compiled binaries for Ubuntu `22.04` and `24.04` [here](https://github.com/nymtech/nyxd/releases).
You can find pre-compiled binaries for Ubuntu `22.04` and `20.04` [here](https://github.com/nymtech/nyxd/releases).
### Manually compiling your validator binary
@@ -109,9 +109,8 @@ Usage:
nyxd [command]
Available Commands:
comet CometBFT subcommands
completion Generate the autocompletion script for the specified shell
config Utilities for managing application configuration
config Create or query an application CLI configuration file
debug Tool for helping with debugging your application
export Export state to JSON
genesis Application's genesis-related subcommands
@@ -120,20 +119,20 @@ Available Commands:
keys Manage your application's keys
prune Prune app history states by keeping the recent heights and deleting old heights
query Querying subcommands
rollback rollback Cosmos SDK and CometBFT state by one height
snapshots Manage local snapshots
rollback rollback cosmos-sdk and tendermint state by one height
rosetta spin up a rosetta server
start Run the full node
status Query remote node for status
tendermint Tendermint subcommands
testnet subcommands for starting or configuring local testnets
tx Transactions subcommands
version Print the application binary version information
Flags:
-h, --help help for nyxd
--home string directory for config and data (default "/Users/neo/.nyxd")
--home string directory for config and data
--log_format string The logging format (json|plain) (default "plain")
--log_level string The logging level (trace|debug|info|warn|error|fatal|panic|disabled or '*:<level>,<key>:<level>') (default "info")
--log_no_color Disable colored logs
--log_level string The logging level (trace|debug|info|warn|error|fatal|panic) (default "info")
--trace print out full stack trace on errors
Use "nyxd [command] --help" for more information about a command.
@@ -181,9 +180,8 @@ Usage:
nyxd [command]
Available Commands:
comet CometBFT subcommands
completion Generate the autocompletion script for the specified shell
config Utilities for managing application configuration
config Create or query an application CLI configuration file
debug Tool for helping with debugging your application
export Export state to JSON
genesis Application's genesis-related subcommands
@@ -192,20 +190,20 @@ Available Commands:
keys Manage your application's keys
prune Prune app history states by keeping the recent heights and deleting old heights
query Querying subcommands
rollback rollback Cosmos SDK and CometBFT state by one height
snapshots Manage local snapshots
rollback rollback cosmos-sdk and tendermint state by one height
rosetta spin up a rosetta server
start Run the full node
status Query remote node for status
tendermint Tendermint subcommands
testnet subcommands for starting or configuring local testnets
tx Transactions subcommands
version Print the application binary version information
Flags:
-h, --help help for nyxd
--home string directory for config and data (default "/Users/neo/.nyxd")
--home string directory for config and data
--log_format string The logging format (json|plain) (default "plain")
--log_level string The logging level (trace|debug|info|warn|error|fatal|panic|disabled or '*:<level>,<key>:<level>') (default "info")
--log_no_color Disable colored logs
--log_level string The logging level (trace|debug|info|warn|error|fatal|panic) (default "info")
--trace print out full stack trace on errors
Use "nyxd [command] --help" for more information about a command.
@@ -248,7 +246,7 @@ You can use the following command to download them for the correct network:
wget -O $HOME/.nyxd/config/genesis.json https://nymtech.net/genesis/genesis.json
# Sandbox testnet
curl https://rpc.sandbox.nymtech.net/genesis | jq '.result.genesis' > $HOME/.nyxd/config/genesis.json
curl https://rpc.sandbox.nymtech.net/snapshots/genesis.json | jq '.result.genesis' > $HOME/.nyxd/config/genesis.json
```
### `config.toml` configuration
@@ -514,7 +512,7 @@ nyxd tx slashing unjail
--from="KEYRING_NAME"
--chain-id=nyx
--gas=auto
--gas-adjustment=1.5
--gas-adjustment=1.4
--gas-prices=0.025unyx
```
@@ -525,7 +523,7 @@ nyxd tx slashing unjail
--from="KEYRING_NAME"
--chain-id=sandbox
--gas=auto
--gas-adjustment=1.5
--gas-adjustment=1.4
--gas-prices=0.025unyx
```
+1 -21
View File
@@ -40,7 +40,7 @@ tokio = { workspace = true, features = [
tokio-stream = { workspace = true, features = ["fs"] }
tokio-tungstenite = { workspace = true }
tokio-util = { workspace = true, features = ["codec"] }
#tracing = { workspace = true }
tracing = { workspace = true }
url = { workspace = true, features = ["serde"] }
zeroize = { workspace = true }
@@ -72,26 +72,6 @@ nym-wireguard = { path = "../common/wireguard" }
nym-wireguard-types = { path = "../common/wireguard-types", default-features = false }
defguard_wireguard_rs = { workspace = true }
opentelemetry = { workspace = true, features = ["trace"] }
tracing = { workspace = true, features = [
"std",
"attributes",
"tracing-attributes",
] }
tracing-opentelemetry = { workspace = true }
opentelemetry_sdk = { version = "0.30.0", default-features = false, features = [
"trace",
"rt-tokio",
"experimental_metrics_custom_reader",
] }
opentelemetry-otlp = { workspace = true, features = [
"metrics",
"grpc-tonic",
"tls",
"tls-webpki-roots",
] }
opentelemetry-stdout = { workspace = true, features = ["trace", "metrics"] }
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
@@ -33,8 +33,6 @@ use nym_gateway_storage::traits::SharedKeyGatewayStorage;
use nym_node_metrics::events::MetricsEvent;
use nym_sphinx::DestinationAddressBytes;
use nym_task::TaskClient;
use opentelemetry::trace::TraceContextExt;
use opentelemetry_sdk::trace::{IdGenerator, RandomIdGenerator};
use rand::CryptoRng;
use std::net::SocketAddr;
use std::time::Duration;
@@ -43,8 +41,7 @@ use time::OffsetDateTime;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::time::timeout;
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
use tracing::{debug, error, info, info_span, instrument, warn};
use tracing_opentelemetry::OpenTelemetrySpanExt;
use tracing::*;
#[derive(Debug, Error)]
pub(crate) enum InitialAuthenticationError {
@@ -857,92 +854,6 @@ impl<R, S> FreshHandler<R, S> {
S: AsyncRead + AsyncWrite + Unpin + Send,
R: CryptoRng + RngCore + Send,
{
/*
┌───────────────────────────────────────────────────────┐
│ Incoming Request │
│ trace_id: "abc123..." (from client) │
└────────────────────────┬──────────────────────────────┘
┌───────────────────────────────────────────────────────┐
│ 1. Create SpanContext │
│ ┌─────────────────────────────────────────────┐ │
│ │ SpanContext::new( │ │
│ │ trace_id: "abc123..." (preserved) │ │
│ │ span_id: "new_random_id" │ │
│ │ is_remote: true │ │
│ │ ) │ │
│ └─────────────────────────────────────────────┘ │
└────────────────────────┬──────────────────────────────┘
┌───────────────────────────────────────────────────────┐
│ 2. Convert to Context │
│ Context::current().with_remote_span_context(...) │
└────────────────────────┬──────────────────────────────┘
┌───────────────────────────────────────────────────────┐
│ 3. Create & Configure Span │
│ span = info_span!("authenticate_v1") │
│ span.set_parent(context) // Before entering │
└────────────────────────┬─────────────────────────────-┘
┌───────────────────────────────────────────────────────┐
│ 4. Enter Span │
│ let _enter = span.enter() │
// All child spans inherit trace_id "abc123..." │
└───────────────────────────────────────────────────────┘
*/
let span = if let ClientControlRequest::AuthenticateV2(ref auth_req) = request {
if let Some(ref trace_id) = auth_req.debug_trace_id {
warn!("RAW TRACE ID: {trace_id:?}");
let trace_id = opentelemetry::trace::TraceId::from_hex(&trace_id)
.expect("Invalid trace ID format");
warn!("🫂TraceID: {trace_id}🫂");
// We don't need to try and preserve the SpanID, just the TraceID (right?) so
// just making a new SpanID for the moment
let id_generator = RandomIdGenerator::default();
let span_id = id_generator.new_span_id();
let span_context = opentelemetry::trace::SpanContext::new(
trace_id,
span_id,
opentelemetry::trace::TraceFlags::SAMPLED,
true, // is_remote = true since this comes from another service
Default::default(),
);
let remote_context =
opentelemetry::Context::current().with_remote_span_context(span_context);
let _context_guard = remote_context.clone().attach();
let span = info_span!(
"authenticate_v2",
trace_id = %trace_id
);
span.set_parent(remote_context.clone());
Some(span)
} else {
warn!("AuthenticateV2 request but no trace_id provided");
None
}
} else {
warn!("Not an AuthenticateV2 request");
None
};
// Probably a nicer way to do this but for now just match
let _guard = match &span {
Some(s) => {
warn!("ENTERED SPAN");
Some(s.enter())
}
None => {
warn!("COULDN'T ENTER SPAN");
None
}
};
// we can handle stateless client requests without prior authentication, like `ClientControlRequest::SupportedProtocol`
let auth_result = match request {
ClientControlRequest::Authenticate {
@@ -950,7 +861,6 @@ impl<R, S> FreshHandler<R, S> {
address,
enc_address,
iv,
debug_trace_id: _,
} => {
self.handle_legacy_authenticate(protocol_version, address, enc_address, iv)
.await
@@ -1,44 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n date_utc as \"date_utc!\",\n SUM(total_stake) as \"total_stake!: i64\",\n SUM(packets_received) as \"total_packets_received!: i64\",\n SUM(packets_sent) as \"total_packets_sent!: i64\",\n SUM(packets_dropped) as \"total_packets_dropped!: i64\"\n FROM (\n SELECT\n date_utc,\n n.total_stake,\n n.packets_received,\n n.packets_sent,\n n.packets_dropped\n FROM nym_node_daily_mixing_stats n\n UNION ALL\n SELECT\n m.date_utc,\n m.total_stake,\n m.packets_received,\n m.packets_sent,\n m.packets_dropped\n FROM mixnode_daily_stats m\n LEFT JOIN nym_node_daily_mixing_stats ON m.mix_id = nym_node_daily_mixing_stats.node_id\n WHERE nym_node_daily_mixing_stats.node_id IS NULL\n )\n GROUP BY date_utc\n ORDER BY date_utc ASC\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "date_utc!",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "total_stake!: i64",
"type_info": "Numeric"
},
{
"ordinal": 2,
"name": "total_packets_received!: i64",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "total_packets_sent!: i64",
"type_info": "Int8"
},
{
"ordinal": 4,
"name": "total_packets_dropped!: i64",
"type_info": "Int8"
}
],
"parameters": {
"Left": []
},
"nullable": [
null,
null,
null,
null,
null
]
},
"hash": "124d45b9604439584650f401607c46bdbd162c7c689f74fe9ddfdfd48f5ddc07"
}
@@ -0,0 +1,44 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n date_utc as \"date_utc!\",\n SUM(total_stake)::BIGINT as \"total_stake!: i64\",\n SUM(packets_received)::BIGINT as \"total_packets_received!: i64\",\n SUM(packets_sent)::BIGINT as \"total_packets_sent!: i64\",\n SUM(packets_dropped)::BIGINT as \"total_packets_dropped!: i64\"\n FROM (\n SELECT\n date_utc,\n n.total_stake,\n n.packets_received,\n n.packets_sent,\n n.packets_dropped\n FROM nym_node_daily_mixing_stats n\n UNION ALL\n SELECT\n m.date_utc,\n m.total_stake,\n m.packets_received,\n m.packets_sent,\n m.packets_dropped\n FROM mixnode_daily_stats m\n LEFT JOIN nym_node_daily_mixing_stats ON m.mix_id = nym_node_daily_mixing_stats.node_id\n WHERE nym_node_daily_mixing_stats.node_id IS NULL\n )\n GROUP BY date_utc\n ORDER BY date_utc ASC\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "date_utc!",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "total_stake!: i64",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "total_packets_received!: i64",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "total_packets_sent!: i64",
"type_info": "Int8"
},
{
"ordinal": 4,
"name": "total_packets_dropped!: i64",
"type_info": "Int8"
}
],
"parameters": {
"Left": []
},
"nullable": [
null,
null,
null,
null,
null
]
},
"hash": "a19da2073bc691fe5cd2119a9527c16d6a8806ba50dcb759ac705b1a19288ca9"
}
@@ -3,7 +3,7 @@
[package]
name = "nym-node-status-api"
version = "3.2.2"
version = "3.2.6"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -22,6 +22,7 @@ clap = { workspace = true, features = ["cargo", "derive", "env", "string"] }
cosmwasm-std = { workspace = true }
envy = { workspace = true }
futures-util = { workspace = true }
hex = { workspace = true }
itertools = { workspace = true }
moka = { workspace = true, features = ["future"] }
# TODO dz had to switch to cheddar versions because develop is ahead of current
@@ -101,6 +101,7 @@ pub(crate) async fn get_all_mixnodes(pool: &DbPool) -> anyhow::Result<Vec<Mixnod
Ok(items)
}
#[cfg(feature = "sqlite")]
pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailyStats>> {
let mut conn = pool.acquire().await?;
let items = sqlx::query_as!(
@@ -142,6 +143,48 @@ pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailySt
Ok(items)
}
#[cfg(feature = "pg")]
pub(crate) async fn get_daily_stats(pool: &DbPool) -> anyhow::Result<Vec<DailyStats>> {
let mut conn = pool.acquire().await?;
let items = sqlx::query_as!(
DailyStats,
r#"
SELECT
date_utc as "date_utc!",
SUM(total_stake)::BIGINT as "total_stake!: i64",
SUM(packets_received)::BIGINT as "total_packets_received!: i64",
SUM(packets_sent)::BIGINT as "total_packets_sent!: i64",
SUM(packets_dropped)::BIGINT as "total_packets_dropped!: i64"
FROM (
SELECT
date_utc,
n.total_stake,
n.packets_received,
n.packets_sent,
n.packets_dropped
FROM nym_node_daily_mixing_stats n
UNION ALL
SELECT
m.date_utc,
m.total_stake,
m.packets_received,
m.packets_sent,
m.packets_dropped
FROM mixnode_daily_stats m
LEFT JOIN nym_node_daily_mixing_stats ON m.mix_id = nym_node_daily_mixing_stats.node_id
WHERE nym_node_daily_mixing_stats.node_id IS NULL
)
GROUP BY date_utc
ORDER BY date_utc ASC
"#,
)
.fetch(&mut *conn)
.try_collect::<Vec<DailyStats>>()
.await?;
Ok(items)
}
pub(crate) async fn get_bonded_mix_ids(pool: &DbPool) -> anyhow::Result<HashSet<i64>> {
let mut conn = pool.acquire().await?;
let items = crate::db::query(
@@ -6,7 +6,7 @@ use tower_http::cors::CorsLayer;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
use crate::http::{server::HttpServer, state::AppState};
use crate::http::{middleware, server::HttpServer, state::AppState};
pub(crate) mod dvpn;
pub(crate) mod gateways;
@@ -63,17 +63,23 @@ impl RouterBuilder {
pub(crate) fn with_state(self, state: AppState) -> RouterWithState {
RouterWithState {
router: self.finalize_routes().with_state(state),
router: self.finalize_routes(state),
}
}
fn finalize_routes(self) -> Router<AppState> {
fn finalize_routes(self, state: AppState) -> Router {
// layers added later wrap earlier layers
self.unfinished_router
// Add response headers middleware
.layer(axum::middleware::from_fn_with_state(
state.clone(),
middleware::add_response_headers,
))
// CORS layer needs to wrap other API layers
.layer(setup_cors())
// logger should be outermost layer
.layer(axum::middleware::from_fn(log_request_debug))
.with_state(state)
}
}
@@ -127,12 +133,4 @@ mod tests {
let _test_router = unfinished_router;
}
#[test]
fn test_router_builder_finalize() {
let router_builder = RouterBuilder::with_default_routes();
let finalized = router_builder.finalize_routes();
// This tests that finalize_routes produces a valid Router
let _router = finalized;
}
}
@@ -265,7 +265,7 @@ fn authenticate(request: &impl VerifiableRequest, state: &AppState) -> HttpResul
Ok(())
}
static FRESHNESS_CUTOFF: time::Duration = time::Duration::minutes(2);
static FRESHNESS_CUTOFF: time::Duration = time::Duration::minutes(5);
fn is_fresh(request_time: &i64) -> HttpResult<()> {
// if a request took longer than N minutes to reach NS API, something is very wrong
@@ -0,0 +1,34 @@
use axum::{
extract::{Request, State},
http::{HeaderName, HeaderValue},
middleware::Next,
response::Response,
};
use time::OffsetDateTime;
use crate::http::state::AppState;
pub async fn add_response_headers(
State(state): State<AppState>,
request: Request,
next: Next,
) -> Response {
let mut response = next.run(request).await;
let headers = response.headers_mut();
// Add timestamp header (RFC3339 format)
let timestamp = OffsetDateTime::now_utc()
.format(&time::format_description::well_known::Rfc3339)
.unwrap_or_else(|_| "unknown".to_string());
if let Ok(value) = HeaderValue::from_str(&timestamp) {
headers.insert(HeaderName::from_static("x-response-timestamp"), value);
}
// Add instance ID header
if let Ok(value) = HeaderValue::from_str(state.instance_id()) {
headers.insert(HeaderName::from_static("x-instance-id"), value);
}
response
}
@@ -3,6 +3,7 @@ use utoipa::ToSchema;
pub(crate) mod api;
pub(crate) mod api_docs;
pub(crate) mod error;
pub(crate) mod middleware;
pub(crate) mod models;
pub(crate) mod server;
pub(crate) mod state;
@@ -34,6 +34,7 @@ pub(crate) struct AppState {
node_geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
bin_info: BinaryInfo,
instance_id: String,
}
impl AppState {
@@ -53,6 +54,7 @@ impl AppState {
node_geocache,
node_delegations,
bin_info: BinaryInfo::new(),
instance_id: generate_instance_id(),
}
}
@@ -94,6 +96,10 @@ impl AppState {
pub(crate) fn build_information(&self) -> &BinaryBuildInformationOwned {
&self.bin_info.build_info
}
pub(crate) fn instance_id(&self) -> &str {
&self.instance_id
}
}
static GATEWAYS_LIST_KEY: &str = "gateways";
@@ -499,7 +505,10 @@ impl HttpCache {
None => {
let new_node_stats = crate::db::queries::get_daily_stats(db)
.await
.unwrap_or_default()
.unwrap_or_else(|e| {
tracing::error!("Failed to get daily stats from database: {}", e);
vec![]
})
.into_iter()
.rev()
.collect::<Vec<_>>();
@@ -698,3 +707,14 @@ impl BinaryInfo {
pub(crate) struct HealthInfo {
pub(crate) uptime: i64,
}
fn generate_instance_id() -> String {
use rand::Rng;
let mut rng = rand::thread_rng();
let random_bytes: [u8; 16] = rng.gen();
format!(
"ns-api-{}-{}",
hex::encode(&random_bytes[..8]),
std::process::id()
)
}
+1 -14
View File
@@ -35,7 +35,7 @@ serde_json.workspace = true
thiserror.workspace = true
tracing.workspace = true
tracing-indicatif = { workspace = true }
tracing-subscriber = { workspace = true, features = ["json", "fmt"] }
tracing-subscriber.workspace = true
tokio = { workspace = true, features = ["macros", "sync", "rt-multi-thread"] }
tokio-util = { workspace = true, features = ["codec"] }
toml = { workspace = true }
@@ -114,19 +114,6 @@ blake2 = "=0.8.1"
sha2 = { workspace = true }
hkdf = { workspace = true }
# signoz
opentelemetry = { workspace = true }
opentelemetry_sdk = { workspace = true }
tracing-core = { workspace = true }
tracing-opentelemetry = { workspace = true }
tracing-serde = { workspace = true }
opentelemetry-otlp = { version = "0.30.0" }
opentelemetry-semantic-conventions = { workspace = true, features = [
"semconv_experimental",
] }
opentelemetry-stdout = { workspace = true, features = ["trace", "metrics"] }
chrono = { workspace = true }
[[bench]]
name = "benchmarks"
harness = false
+16 -68
View File
@@ -6,13 +6,11 @@ use crate::cli::commands::{
test_throughput,
};
use crate::env::vars::{NYMNODE_CONFIG_ENV_FILE_ARG, NYMNODE_NO_BANNER_ARG};
use crate::error::NymNodeError;
use crate::logging::{setup_no_otel_logger, setup_tracing_logger};
use crate::logging::setup_tracing_logger;
use clap::{Parser, Subcommand};
use nym_bin_common::bin_info;
use std::future::Future;
use std::sync::OnceLock;
use tracing::instrument;
pub(crate) mod commands;
mod helpers;
@@ -55,73 +53,28 @@ impl Cli {
.block_on(fut))
}
#[instrument]
pub(crate) fn execute(self) -> anyhow::Result<()> {
// NOTE: `test_throughput` sets up its own logger as it has to include additional layers
if !matches!(self.command, Commands::TestThroughput(..)) {
setup_tracing_logger()?;
}
match self.command {
// Sync commands get logger w. no OTEL
Commands::BuildInfo(args) => {
setup_no_otel_logger()?;
build_info::execute(args)?
Commands::BuildInfo(args) => build_info::execute(args)?,
Commands::BondingInformation(args) => {
{ Self::execute_async(bonding_information::execute(args))? }?
}
Commands::Migrate(args) => {
setup_no_otel_logger()?;
migrate::execute(*args)?
Commands::NodeDetails(args) => { Self::execute_async(node_details::execute(args))? }?,
Commands::Run(args) => { Self::execute_async(run::execute(*args))? }?,
Commands::Migrate(args) => migrate::execute(*args)?,
Commands::Sign(args) => { Self::execute_async(sign::execute(args))? }?,
Commands::TestThroughput(args) => test_throughput::execute(args)?,
Commands::UnsafeResetSphinxKeys(args) => {
{ Self::execute_async(reset_sphinx_keys::execute(args))? }?
}
Commands::TestThroughput(args) => {
// Has its own logging setup
test_throughput::execute(args)?
}
// SigNoz/OTEL run in async context
Commands::BondingInformation(args) => Self::execute_async(async move {
setup_tracing_logger().map_err(NymNodeError::TracingSetupFailure)?;
bonding_information::execute(args).await
})??,
Commands::NodeDetails(args) => Self::execute_async(async move {
setup_tracing_logger().map_err(NymNodeError::TracingSetupFailure)?;
node_details::execute(args).await
})??,
Commands::Run(args) => Self::execute_async(async move {
setup_tracing_logger().map_err(NymNodeError::TracingSetupFailure)?;
run::execute(*args).await
})??,
Commands::Sign(args) => Self::execute_async(async move {
setup_tracing_logger().map_err(NymNodeError::TracingSetupFailure)?;
sign::execute(args).await
})??,
Commands::UnsafeResetSphinxKeys(args) => Self::execute_async(async move {
setup_tracing_logger().map_err(NymNodeError::TracingSetupFailure)?;
reset_sphinx_keys::execute(args).await
})??,
Commands::TestTracing => Self::execute_async(async move {
setup_tracing_logger().map_err(NymNodeError::TracingSetupFailure)?;
Ok::<(), crate::error::NymNodeError>(())
})??,
}
Ok(())
}
// pub(crate) fn execute(self) -> anyhow::Result<()> {
// // NOTE: `test_throughput` sets up its own logger as it has to include additional layers
// // if !matches!(self.command, Commands::TestThroughput(..)) {
// // setup_tracing_logger()?;
// // }
// match self.command {
// Commands::BuildInfo(args) => build_info::execute(args)?,
// Commands::BondingInformation(args) => {
// { Self::execute_async(bonding_information::execute(args))? }?
// }
// Commands::NodeDetails(args) => { Self::execute_async(node_details::execute(args))? }?,
// Commands::Run(args) => { Self::execute_async(run::execute(*args))? }?,
// Commands::Migrate(args) => migrate::execute(*args)?,
// Commands::Sign(args) => { Self::execute_async(sign::execute(args))? }?,
// Commands::TestThroughput(args) => test_throughput::execute(args)?,
// Commands::UnsafeResetSphinxKeys(args) => {
// { Self::execute_async(reset_sphinx_keys::execute(args))? }?
// }
// }
// Ok(())
// }
}
#[derive(Subcommand, Debug)]
@@ -151,11 +104,6 @@ pub(crate) enum Commands {
/// was running on this machine in mixnet mode
#[clap(hide = true)]
TestThroughput(test_throughput::Args),
/// Test local tracing for instrumentation and
/// TraceID aligmnet
// TODO this needs to just start and then wait for the shutdown signal - grab from run()
TestTracing,
}
#[cfg(test)]
-3
View File
@@ -76,9 +76,6 @@ pub enum KeyIOFailure {
#[derive(Debug, Error)]
pub enum NymNodeError {
#[error("Failed to setup tracing logger")]
TracingSetupFailure(#[source] anyhow::Error),
#[error("this binary version no longer supports migration from legacy mixnodes and gateways")]
UnsupportedMigration,
+4 -135
View File
@@ -2,27 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only
use nym_bin_common::logging::{default_tracing_env_filter, default_tracing_fmt_layer};
use tracing::{info, warn};
use tracing_subscriber::filter::Directive;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{fmt, Layer};
// Signoz
use crate::trace_id_format::TraceIdFormat;
use opentelemetry::trace::TracerProvider;
use opentelemetry::{global, KeyValue};
use opentelemetry_otlp::tonic_types::metadata::MetadataMap;
use opentelemetry_otlp::tonic_types::transport::ClientTlsConfig;
use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
use opentelemetry_sdk::metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider};
use opentelemetry_sdk::trace::{RandomIdGenerator, SdkTracerProvider};
use opentelemetry_sdk::{trace::Sampler, Resource};
use opentelemetry_semantic_conventions::resource::{DEPLOYMENT_ENVIRONMENT_NAME, SERVICE_VERSION};
use opentelemetry_semantic_conventions::SCHEMA_URL;
use tracing_core::Level;
use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer};
use tracing_subscriber::fmt::format::FmtSpan;
pub(crate) fn granual_filtered_env() -> anyhow::Result<tracing_subscriber::filter::EnvFilter> {
fn directive_checked(directive: impl Into<String>) -> anyhow::Result<Directive> {
@@ -31,6 +13,7 @@ pub(crate) fn granual_filtered_env() -> anyhow::Result<tracing_subscriber::filte
let mut filter = default_tracing_env_filter();
// these crates are more granularly filtered
let filter_crates = ["defguard_wireguard_rs"];
for crate_name in filter_crates {
filter = filter.add_directive(directive_checked(format!("{crate_name}=warn"))?);
@@ -39,127 +22,13 @@ pub(crate) fn granual_filtered_env() -> anyhow::Result<tracing_subscriber::filte
}
pub(crate) fn build_tracing_logger() -> anyhow::Result<impl SubscriberExt> {
let key = std::env::var("SIGNOZ_INGESTION_KEY").expect("SIGNOZ_INGESTION_KEY not set");
println!("SIGNOZ_INGESTION_KEY = {}", key);
let mut metadata = MetadataMap::new();
metadata.insert(
"signoz-ingestion-key",
key.parse().expect("Could not parse key"),
);
let tracer_provider = init_tracer_provider(metadata)?;
let meter_provider = init_meter_provider()?;
let tracer = tracer_provider.tracer("tracing-otel-subscriber");
let fmt_layer = fmt::layer()
.json()
.with_writer(std::io::stderr)
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
.with_span_list(false)
.with_current_span(true)
.event_format(TraceIdFormat);
let registry = tracing_subscriber::registry()
.with(fmt_layer)
.with(granual_filtered_env()?)
.with(tracing_subscriber::filter::LevelFilter::from_level(
Level::DEBUG,
))
.with(MetricsLayer::new(meter_provider))
.with(OpenTelemetryLayer::new(tracer));
Ok(registry)
Ok(tracing_subscriber::registry()
.with(default_tracing_fmt_layer(std::io::stderr))
.with(granual_filtered_env()?))
}
pub(crate) fn setup_tracing_logger() -> anyhow::Result<()> {
build_tracing_logger()?.init();
Ok(())
}
// This is called outside of the async context where we can't use OTEL
pub(crate) fn setup_no_otel_logger() -> anyhow::Result<()> {
// Only set up if not already initialized
if tracing::dispatcher::has_been_set() {
// It shouldn't be - this is really checking that it is torn down between async command executions
return Err(anyhow::anyhow!("Tracing logger already initialised"));
}
let registry = tracing_subscriber::registry()
.with(default_tracing_fmt_layer(std::io::stderr))
.with(granual_filtered_env()?)
.with(tracing_subscriber::filter::LevelFilter::from_level(
Level::INFO,
));
registry
.try_init()
.map_err(|e| anyhow::anyhow!("Failed to set tracing subscriber: {}", e))?;
Ok(())
}
// Signoz/OTEL
fn resource() -> Resource {
Resource::builder()
.with_service_name("nym-node")
.with_schema_url(
[
KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
KeyValue::new(DEPLOYMENT_ENVIRONMENT_NAME, "develop"),
],
SCHEMA_URL,
)
.build()
}
fn init_tracer_provider(metadata: MetadataMap) -> anyhow::Result<SdkTracerProvider> {
let endpoint = std::env::var("SIGNOZ_ENDPOINT").expect("SIGNOZ_ENDPOINT not set");
info!("SIGNOZ_ENDPOINT = {}", endpoint);
let mut exporter_builder = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_metadata(metadata)
.with_endpoint(&endpoint);
if endpoint.starts_with("https://") {
exporter_builder =
exporter_builder.with_tls_config(ClientTlsConfig::new().with_enabled_roots());
}
let exporter = exporter_builder.build()?;
let tracer = SdkTracerProvider::builder()
.with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
1.0,
))))
.with_id_generator(RandomIdGenerator::default())
.with_resource(resource())
.with_batch_exporter(exporter)
.build();
Ok(tracer)
}
fn init_meter_provider() -> anyhow::Result<SdkMeterProvider> {
let exporter = opentelemetry_otlp::MetricExporter::builder()
.with_tonic()
.with_temporality(opentelemetry_sdk::metrics::Temporality::default())
.build()?;
let reader = PeriodicReader::builder(exporter)
.with_interval(std::time::Duration::from_secs(30))
.build();
let stdout_reader =
PeriodicReader::builder(opentelemetry_stdout::MetricExporter::default()).build();
let meter_provider = MeterProviderBuilder::default()
.with_resource(resource())
.with_reader(reader)
.with_reader(stdout_reader)
.build();
global::set_meter_provider(meter_provider.clone());
Ok(meter_provider)
}
-1
View File
@@ -16,7 +16,6 @@ pub(crate) mod error;
mod logging;
pub(crate) mod node;
pub(crate) mod throughput_tester;
mod trace_id_format;
pub(crate) mod wireguard;
fn main() -> anyhow::Result<()> {
-88
View File
@@ -1,88 +0,0 @@
use chrono::Utc;
use opentelemetry::trace::TraceContextExt;
use opentelemetry::{SpanId, TraceId};
use serde::ser::{SerializeMap, Serializer as _};
use std::io;
use tracing::{Event, Subscriber};
use tracing_opentelemetry::OpenTelemetrySpanExt;
use tracing_serde::fields::AsMap;
use tracing_serde::AsSerde;
use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields};
use tracing_subscriber::registry::LookupSpan;
pub struct WriteAdaptor<'a> {
fmt_write: &'a mut dyn std::fmt::Write,
}
impl<'a> WriteAdaptor<'a> {
pub fn new(fmt_write: &'a mut dyn std::fmt::Write) -> Self {
Self { fmt_write }
}
}
impl<'a> io::Write for WriteAdaptor<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let s =
std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
self.fmt_write
.write_str(s)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(s.as_bytes().len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub struct TraceIdFormat;
impl<S, N> FormatEvent<S, N> for TraceIdFormat
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
N: for<'writer> FormatFields<'writer> + 'static,
{
fn format_event(
&self,
_ctx: &FmtContext<'_, S, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> std::fmt::Result
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
let meta = event.metadata();
let mut visit = || {
let mut serializer = serde_json::Serializer::new(WriteAdaptor::new(&mut writer));
let mut serializer = serializer.serialize_map(None)?;
serializer.serialize_entry("timestamp", &Utc::now().to_rfc3339())?;
serializer.serialize_entry("level", &meta.level().as_serde())?;
serializer.serialize_entry("fields", &event.field_map())?;
serializer.serialize_entry("target", meta.target())?;
let current_span = tracing::Span::current();
let context = current_span.context();
let span_ref = context.span();
let span_context = span_ref.span_context();
let trace_id = span_context.trace_id();
if trace_id != TraceId::INVALID {
serializer.serialize_entry("trace_id", &trace_id.to_string())?;
let span_id = span_context.span_id();
if span_id != SpanId::INVALID {
serializer.serialize_entry("span_id", &span_id.to_string())?;
}
}
serializer.end()
};
visit().map_err(|_| std::fmt::Error)?;
writeln!(writer)
}
}
@@ -47,7 +47,6 @@ test_port_range_rules() {
"8087-8088:tcp:Simplify Media"
"8232-8233:tcp:Zcash"
"8332-8333:tcp:Bitcoin"
"18080-18081:tcp:Monero"
)
local total_failures=0
@@ -149,7 +148,7 @@ test_critical_services() {
# Verify default reject rule exists
test_default_reject_rule() {
echo -e "${YELLOW}This test takes some time, do not quit the process${NC}"
echo
echo
echo -e "${YELLOW}Testing Default Reject Rule...${NC}"
# Try different patterns to detect the reject rule
@@ -237,4 +236,4 @@ if [[ $EUID -ne 0 ]]; then
fi
# Run the tests
run_all_tests "$@"
run_all_tests "$@"
@@ -357,13 +357,9 @@ apply_port_allowlist() {
["Lightning"]="9735"
["NDMP"]="10000"
["OpenPGP"]="11371"
["Monero"]="18080-18081"
["MoneroRPC"]="18089"
["GoogleVoice"]="19294"
["EnsimControlPanel"]="19638"
["DarkFiTor"]="25551"
["Minecraft"]="25565"
["DarkFi"]="26661"
["Steam"]="27000-27050"
["ElectrumSSL"]="50002"
["MOSH"]="60000-61000"
@@ -676,4 +672,4 @@ main() {
esac
}
main "$@"
main "$@"
+2 -16
View File
@@ -70,18 +70,10 @@ tokio-util.workspace = true
uuid = { workspace = true, features = ["v4", "serde"] }
bincode = { workspace = true }
serde = { workspace = true, features = ["derive"] }
#tracing.workspace = true
#tracing-subscriber = { workspace = true, features = ["env-filter", "registry", "std"] }
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["env-filter"] }
dirs.workspace = true
opentelemetry = { workspace = true }
opentelemetry_sdk = { workspace = true }
tracing = { workspace = true, features = ["std"] }
tracing-subscriber = { workspace = true, features = ["registry", "std"] }
tracing-core = { workspace = true }
tracing-opentelemetry = { workspace = true }
[dev-dependencies]
anyhow = { workspace = true }
dotenvy = { workspace = true }
@@ -91,11 +83,6 @@ tokio = { workspace = true, features = ["full"] }
time = { workspace = true }
nym-bin-common = { path = "../../../common/bin-common", features = ["basic_tracing"] }
opentelemetry-semantic-conventions = { workspace = true, features = ["semconv_experimental"] }
opentelemetry-otlp = { workspace = true, features = ["metrics", "grpc-tonic", "tls",
"tls-webpki-roots", ] }
opentelemetry-stdout = { workspace = true, features = ["trace", "metrics"] }
# extra dependencies for libp2p examples
#libp2p = { git = "https://github.com/ChainSafe/rust-libp2p.git", rev = "e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6", features = [ "identify", "macros", "ping", "tokio", "tcp", "dns", "websocket", "noise", "mplex", "yamux", "gossipsub" ]}
tokio-stream = { workspace = true }
@@ -105,4 +92,3 @@ hex = { workspace = true }
[features]
libp2p-vanilla = []
+10 -192
View File
@@ -1,208 +1,28 @@
use dotenvy::dotenv;
use nym_sdk::mixnet::{
AnonymousSenderTag, MixnetClientBuilder, MixnetMessageSender, ReconstructedMessage,
StoragePaths,
};
use opentelemetry::trace::TraceContextExt;
use opentelemetry::trace::Tracer;
use opentelemetry::trace::TracerProvider;
use opentelemetry::Context;
use opentelemetry::{global, KeyValue};
use opentelemetry_otlp::tonic_types::metadata::MetadataMap;
use opentelemetry_otlp::tonic_types::transport::ClientTlsConfig;
use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
use opentelemetry_sdk::metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider};
use opentelemetry_sdk::trace::IdGenerator;
use opentelemetry_sdk::trace::{RandomIdGenerator, SdkTracerProvider};
use opentelemetry_sdk::{trace::Sampler, Resource};
use opentelemetry_semantic_conventions::{
attribute::{DEPLOYMENT_ENVIRONMENT_NAME, SERVICE_VERSION},
SCHEMA_URL,
};
use std::path::PathBuf;
use tempfile::TempDir;
use tracing::info_span;
use tracing::instrument;
use tracing::{info, warn};
use tracing_core::Level;
use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
fn resource() -> Resource {
Resource::builder()
.with_service_name("sdk-example-surb-reply")
.with_schema_url(
[
KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
KeyValue::new(DEPLOYMENT_ENVIRONMENT_NAME, "develop"),
],
SCHEMA_URL,
)
.build()
}
fn init_tracer_provider(metadata: MetadataMap) -> anyhow::Result<SdkTracerProvider> {
let endpoint = std::env::var("SIGNOZ_ENDPOINT").expect("SIGNOZ_ENDPOINT not set");
println!("SIGNOZ_ENDPOINT = {}", endpoint);
// Configure OTLP exporter with metadata
let mut exporter_builder = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_metadata(metadata)
.with_endpoint(&endpoint);
// Try with TLS - seems to work
if endpoint.starts_with("https://") {
exporter_builder =
exporter_builder.with_tls_config(ClientTlsConfig::new().with_enabled_roots());
}
let exporter = exporter_builder.build()?;
let tracer = SdkTracerProvider::builder()
// Customize sampling strategy
.with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
1.0,
))))
// If export trace to AWS X-Ray, you can use XrayIdGenerator
.with_id_generator(RandomIdGenerator::default())
.with_resource(resource())
.with_batch_exporter(exporter)
.build();
// We set this in meter provider but didn't in here
// :facepalm:
global::set_tracer_provider(tracer.clone());
Ok(tracer)
}
fn init_meter_provider() -> SdkMeterProvider {
let exporter = opentelemetry_otlp::MetricExporter::builder()
.with_tonic()
.with_temporality(opentelemetry_sdk::metrics::Temporality::default())
.build()
.unwrap();
let reader = PeriodicReader::builder(exporter)
.with_interval(std::time::Duration::from_secs(30))
.build();
// For debugging in development
let stdout_reader =
PeriodicReader::builder(opentelemetry_stdout::MetricExporter::default()).build();
let meter_provider = MeterProviderBuilder::default()
.with_resource(resource())
.with_reader(reader)
.with_reader(stdout_reader)
.build();
global::set_meter_provider(meter_provider.clone());
meter_provider
}
fn init_tracing_subscriber(metadata: MetadataMap) -> OtelGuard {
let tracer_provider = init_tracer_provider(metadata).expect("Initializing tracer failed");
let meter_provider = init_meter_provider();
let tracer = tracer_provider.tracer("tracing-otel-subscriber");
tracing_subscriber::registry()
// The global level filter prevents the exporter network stack
// from reentering the globally installed OpenTelemetryLayer with
// its own spans while exporting, as the libraries should not use
// tracing levels below DEBUG. If the OpenTelemetry layer needs to
// trace spans and events with higher verbosity levels, consider using
// per-layer filtering to target the telemetry layer specifically,
// e.g. by target matching.
.with(tracing_subscriber::filter::LevelFilter::from_level(
Level::DEBUG,
))
.with(tracing_subscriber::fmt::layer())
.with(MetricsLayer::new(meter_provider.clone()))
.with(OpenTelemetryLayer::new(tracer))
.init();
OtelGuard {
tracer_provider,
meter_provider,
}
}
struct OtelGuard {
tracer_provider: SdkTracerProvider,
meter_provider: SdkMeterProvider,
}
impl Drop for OtelGuard {
fn drop(&mut self) {
if let Err(err) = self.tracer_provider.shutdown() {
eprintln!("{err:?}");
}
if let Err(err) = self.meter_provider.shutdown() {
eprintln!("{err:?}");
}
}
}
#[tokio::main]
#[instrument]
async fn main() -> anyhow::Result<()> {
// nym_bin_common::logging::setup_tracing_logger();
dotenv().ok();
let key = std::env::var("SIGNOZ_INGESTION_KEY").expect("SIGNOZ_INGESTION_KEY not set");
println!("SIGNOZ_INGESTION_KEY = {}", key);
let mut metadata = MetadataMap::new();
metadata.insert("signoz-ingestion-key", key.parse()?);
let _guard = init_tracing_subscriber(metadata);
let tracer = global::tracer("sdk-example-surb-reply");
let span = tracer.start("test_span");
let cx = Context::current_with_span(span);
let _guard = cx.clone().attach();
let trace_id = cx.span().span_context().trace_id();
warn!("Main TRACE_ID: {:?}", trace_id);
let span = info_span!(
"surb_reply_example_session",
trace_id = %trace_id.to_string()
);
let _enter = span.enter();
let otel_context = opentelemetry::Context::current();
warn!("OTEL CONTEXT: {:?}", otel_context);
let span = otel_context.span();
let context = span.span_context();
let trace_id = context.trace_id();
warn!("TRACE_ID: {:?}", trace_id);
// panic!();
async fn main() {
nym_bin_common::logging::setup_tracing_logger();
// Specify some config options
// let config_dir: PathBuf = TempDir::new().unwrap().path().to_path_buf();
// let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap();
let config_dir: PathBuf = TempDir::new().unwrap().path().to_path_buf();
let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap();
// Create the client with a storage backend, and enable it by giving it some paths. If keys
// exists at these paths, they will be loaded, otherwise they will be generated.
// let client = MixnetClientBuilder::new_with_default_storage(storage_paths)
// .await
// .unwrap()
// .build()
// .unwrap();
let client_builder = MixnetClientBuilder::new_ephemeral();
let mixnet_client = client_builder
.request_gateway("BAF2aYpzcK9KbSS3Y7EdLisxiogkTr88FXkdL8EDNigH".to_string())
.with_ignore_epoch_roles(true)
.with_extended_topology(true)
.build()?;
let client = MixnetClientBuilder::new_with_default_storage(storage_paths)
.await
.unwrap()
.build()
.unwrap();
// Now we connect to the mixnet, using keys now stored in the paths provided.
let mut client = mixnet_client.connect_to_mixnet().await.unwrap();
let mut client = client.connect_to_mixnet().await.unwrap();
// Be able to get our client address
let our_address = client.nym_address();
@@ -249,6 +69,4 @@ async fn main() -> anyhow::Result<()> {
client
.on_messages(|msg| println!("\nReceived: {}", String::from_utf8_lossy(&msg.message)))
.await;
Ok(())
}
-19
View File
@@ -396,21 +396,6 @@ where
derivation_material: Option<DerivationMaterial>,
}
impl<S> std::fmt::Debug for DisconnectedMixnetClient<S>
where
S: MixnetClientStorage + Clone + 'static,
S::ReplyStore: Send + Sync,
S::GatewaysDetailsStore: Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Send + Sync,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DisconnectedMixnetClient").finish()
}
}
impl<S> DisconnectedMixnetClient<S>
where
S: MixnetClientStorage + Clone + 'static,
@@ -489,7 +474,6 @@ where
.collect()
}
#[tracing::instrument]
async fn setup_client_keys(&self) -> Result<()> {
let mut rng = OsRng;
let key_store = self.storage.key_store();
@@ -587,7 +571,6 @@ where
///
/// This function will return an error if you try to re-register when in an already registered
/// state.
#[tracing::instrument]
pub async fn setup_gateway(&mut self) -> Result<()> {
if !matches!(self.state, BuilderState::New) {
return Err(Error::ReregisteringGatewayNotSupported);
@@ -678,7 +661,6 @@ where
)
}
#[tracing::instrument]
async fn connect_to_mixnet_common(mut self) -> Result<(BaseClient, Recipient)> {
self.setup_client_keys().await?;
self.setup_gateway().await?;
@@ -829,7 +811,6 @@ where
/// let client = client.connect_to_mixnet().await.unwrap();
/// }
/// ```
#[tracing::instrument]
pub async fn connect_to_mixnet(self) -> Result<MixnetClient> {
if self.socks5_config.is_some() {
return Err(Error::Socks5Config { set: true });
+1 -1
View File
@@ -6,7 +6,7 @@ use url::Url;
const DEFAULT_SDK_CLIENT_ID: &str = "_default-nym-sdk-client";
/// Config struct for [`crate::mixnet::MixnetClient`]
#[derive(Default, Debug)]
#[derive(Default)]
pub struct Config {
/// If the user has explicitly specified a gateway.
pub user_chosen_gateway: Option<String>,
@@ -22,8 +22,6 @@ use nym_task::{
TaskHandle,
};
use nym_topology::{NymRouteProvider, NymTopology};
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
@@ -66,14 +64,6 @@ pub struct MixnetClient {
pub(crate) remember_me: RememberMe,
}
impl Debug for MixnetClient {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("MixnetClient")
.field("address", &self.nym_address)
.finish()
}
}
impl MixnetClient {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
@@ -204,15 +194,7 @@ impl MixnetClient {
}
/// Wait for messages from the mixnet
#[tracing::instrument]
pub async fn wait_for_messages(&mut self) -> Option<Vec<ReconstructedMessage>> {
let span = tracing::info_span!(
"wait_for_messages",
"address = {}",
self.nym_address.to_string()
);
let _enter = span.enter();
self.reconstructed_receiver.next().await
}
-10
View File
@@ -69,9 +69,6 @@ pub trait MixnetMessageSender {
where
M: AsRef<[u8]> + Send,
{
let span = tracing::info_span!("send_message", "address = {}", address.to_string());
let _enter = span.enter();
let lane = TransmissionLane::General;
let input_msg = match surbs {
IncludedSurbs::Amount(surbs) => InputMessage::new_anonymous(
@@ -110,13 +107,6 @@ pub trait MixnetMessageSender {
where
M: AsRef<[u8]> + Send,
{
let span = tracing::info_span!(
"send_reply",
"recipient_tag = {}",
recipient_tag.to_string()
);
let _enter = span.enter();
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_reply(
recipient_tag,
@@ -1 +0,0 @@
.env