Compare commits

..

3 Commits

Author SHA1 Message Date
benedettadavico 867acfef26 .. 2025-10-22 10:02:17 +02:00
benedettadavico e4996dc0ce comment out migration 2025-10-21 14:28:01 +02:00
Jędrzej Stuczyński a6e23a210b bugfix: update stored epoch share when changing ownership 2025-10-21 11:10:24 +01:00
243 changed files with 11205 additions and 9370 deletions
-3
View File
@@ -8,13 +8,10 @@ on:
- 'gateway/**'
- 'integrations/**'
- 'nym-api/**'
- 'nym-authenticator-client/**'
- 'nym-credential-proxy/**'
- 'nym-ip-packet-client/**'
- 'nym-network-monitor/**'
- 'nym-node/**'
- 'nym-node-status-api/**'
- 'nym-registration-client/**'
- 'nym-statistics-api/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
+9 -10
View File
@@ -6,14 +6,16 @@ on:
paths:
- "ts-packages/**"
- "sdk/typescript/**"
- "nym-connect/desktop/src/**"
- "nym-connect/desktop/package.json"
- "nym-wallet/src/**"
- "nym-wallet/package.json"
- "explorer-v2/**"
- "explorer/**"
- ".github/workflows/ci-lint-typescript.yml"
jobs:
build:
runs-on: arc-linux-latest
runs-on: ubuntu-22.04
env:
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
@@ -23,7 +25,6 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup yarn
run: npm install -g yarn
@@ -36,12 +37,14 @@ jobs:
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install wasm-opt
run: cargo install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '116'
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v5
with:
go-version: "1.24.6"
go-version: "1.23.7"
- name: Install
run: yarn
@@ -49,11 +52,7 @@ jobs:
- name: Build packages
run: yarn build:ci
- name: Install again
run: yarn
- name: Lint
run: yarn lint
- name: Typecheck with tsc
run: yarn tsc
@@ -8,7 +8,7 @@ on:
jobs:
build:
runs-on: arc-linux-latest
runs-on: custom-linux
steps:
- uses: actions/checkout@v4
+4 -7
View File
@@ -4,7 +4,7 @@ on:
jobs:
publish:
runs-on: ubuntu-latest
runs-on: arc-ubuntu-22.04
steps:
- uses: actions/checkout@v4
@@ -17,13 +17,10 @@ jobs:
- name: Setup yarn
run: npm install -g yarn
- name: Install rust toolchain
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
@@ -32,9 +29,9 @@ jobs:
run: cargo install wasm-opt
- name: Set up Go
uses: actions/setup-go@v6
uses: actions/setup-go@v5
with:
go-version: "1.24.6"
go-version: "1.23.7"
- name: Install dependencies
run: yarn
+30 -3
View File
@@ -3,6 +3,11 @@ name: Build and upload Node Status agent container to harbor.nymte.ch
on:
workflow_dispatch:
inputs:
gateway_probe_git_ref:
type: string
default: nym-vpn-core-v1.4.0
required: true
description: Which gateway probe git ref to build the image with
release_image:
description: 'Tag image as a release'
required: true
@@ -38,6 +43,16 @@ jobs:
VERSION=$(yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml)
echo "result=$VERSION" >> $GITHUB_OUTPUT
- name: cleanup-gateway-probe-ref
id: cleanup_gateway_probe_ref
run: |
GATEWAY_PROBE_GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }}
GIT_REF_SLUG="${GATEWAY_PROBE_GIT_REF//\//-}"
echo "git_ref=${GIT_REF_SLUG}" >> $GITHUB_OUTPUT
- name: Set GIT_TAG variable
run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV
- name: Initialize RELEASE_TAG
run: echo "RELEASE_TAG=" >> $GITHUB_ENV
@@ -46,12 +61,24 @@ jobs:
run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV
- name: Set IMAGE_NAME_AND_TAGS variable
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV
- name: New env vars
run: echo "RELEASE_TAG='$RELEASE_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
# - name: Remove existing tag if exists
# run: |
# if git rev-parse $${{ env.GIT_TAG }} >/dev/null 2>&1; then
# git push --delete origin $${{ env.GIT_TAG }}
# git tag -d $${{ env.GIT_TAG }}
# fi
# - name: Create tag
# run: |
# git tag -a $${{ env.GIT_TAG }} -m "Version ${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}"
# git push origin $${{ env.GIT_TAG }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }}
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }}
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
-40
View File
@@ -4,46 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2025.19-kase] (2025-10-30)
- update ns agent workflow ([#6154])
- Cherry pick - request #6143 from nymtech/bugfix/mix-tx-closed-v2 ([#6153])
- bugfix: nym-credential-proxy query params parsing regression ([#6121])
- bugfix: revert some dep updates introduced in #6043 ([#6120])
- Skip ipv6 metadata endpoint request ([#6118])
- update to no longer use 1mb files ([#6117])
- chore: restore pending dkg contract state migration ([#6116])
- Revert "Propagate cancel token to mixnet client" ([#6115])
- Update dirs to 6.0 ([#6109])
- Propagate cancel token to mixnet client ([#6105])
- bugfix: retrieve and update ticketbook in the same query ([#6101])
- bugfix: include network name in the default gateway probe config path ([#6100])
- Bugfix/incompatibility fixes ([#6099])
- [DOCs/operators] QUIC deployment script & docs ([#6098])
- bugfix: testnet manager 02sql migration ([#6096])
- feat: move gateway probe to monorepo (and update to rust edition 2024) ([#6094])
- bugfix: use custom topology provider for list of init gateways ([#6092])
- Max/fix wasm client + build commands ([#6043])
[#6154]: https://github.com/nymtech/nym/pull/6154
[#6153]: https://github.com/nymtech/nym/pull/6153
[#6121]: https://github.com/nymtech/nym/pull/6121
[#6120]: https://github.com/nymtech/nym/pull/6120
[#6118]: https://github.com/nymtech/nym/pull/6118
[#6117]: https://github.com/nymtech/nym/pull/6117
[#6116]: https://github.com/nymtech/nym/pull/6116
[#6115]: https://github.com/nymtech/nym/pull/6115
[#6109]: https://github.com/nymtech/nym/pull/6109
[#6105]: https://github.com/nymtech/nym/pull/6105
[#6101]: https://github.com/nymtech/nym/pull/6101
[#6100]: https://github.com/nymtech/nym/pull/6100
[#6099]: https://github.com/nymtech/nym/pull/6099
[#6098]: https://github.com/nymtech/nym/pull/6098
[#6096]: https://github.com/nymtech/nym/pull/6096
[#6094]: https://github.com/nymtech/nym/pull/6094
[#6092]: https://github.com/nymtech/nym/pull/6092
[#6043]: https://github.com/nymtech/nym/pull/6043
## [2025.18-jarlsberg] (2025-10-14)
- ns-api: add descriptions to dVPN gateway responses ([#6102])
Generated
+17 -48
View File
@@ -133,9 +133,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "ammonia"
version = "4.1.2"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6"
checksum = "d6b346764dd0814805de8abf899fe03065bcee69bb1a4771c785817e39f3978f"
dependencies = [
"cssparser",
"html5ever",
@@ -2262,7 +2262,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.60.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -2579,7 +2579,7 @@ dependencies = [
[[package]]
name = "extension-storage"
version = "1.4.1"
version = "1.4.0-rc.0"
dependencies = [
"bip39",
"console_error_panic_hook",
@@ -4478,7 +4478,7 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.4.1"
version = "1.4.0-rc.0"
dependencies = [
"async-trait",
"futures",
@@ -4488,7 +4488,6 @@ dependencies = [
"nym-ordered-buffer",
"nym-service-providers-common",
"nym-socks5-requests",
"nym-validator-client",
"rand 0.8.5",
"serde",
"serde-wasm-bindgen 0.6.5",
@@ -4825,7 +4824,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.68"
version = "1.1.67"
dependencies = [
"anyhow",
"async-trait",
@@ -5051,7 +5050,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.65"
version = "1.1.64"
dependencies = [
"anyhow",
"base64 0.22.1",
@@ -5134,7 +5133,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.65"
version = "1.1.64"
dependencies = [
"bs58",
"clap",
@@ -5284,7 +5283,7 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.4.1"
version = "1.4.0-rc.0"
dependencies = [
"anyhow",
"futures",
@@ -5463,11 +5462,8 @@ dependencies = [
"nym-crypto",
"nym-ecash-contract-common",
"nym-ecash-signer-check",
"nym-http-api-client",
"nym-http-api-common",
"nym-network-defaults",
"nym-pemstore",
"nym-upgrade-mode-check",
"nym-validator-client",
"rand 0.8.5",
"reqwest 0.12.22",
@@ -5538,7 +5534,6 @@ dependencies = [
"nym-http-api-client",
"nym-http-api-common",
"nym-serde-helpers",
"nym-upgrade-mode-check",
"reqwest 0.12.22",
"schemars 0.8.22",
"serde",
@@ -5666,7 +5661,6 @@ dependencies = [
"aead",
"aes",
"aes-gcm-siv",
"anyhow",
"base64 0.22.1",
"blake3",
"bs58",
@@ -5680,12 +5674,10 @@ dependencies = [
"jwt-simple",
"nym-pemstore",
"nym-sphinx-types",
"nym-test-utils",
"rand 0.8.5",
"rand_chacha 0.3.1",
"serde",
"serde_bytes",
"serde_json",
"sha2 0.10.9",
"subtle-encoding",
"thiserror 2.0.12",
@@ -6362,7 +6354,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.66"
version = "1.1.65"
dependencies = [
"addr",
"anyhow",
@@ -6412,7 +6404,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.20.0"
version = "1.19.0"
dependencies = [
"anyhow",
"arc-swap",
@@ -6554,7 +6546,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "4.0.11-rc1"
version = "4.0.10"
dependencies = [
"ammonia",
"anyhow",
@@ -6650,7 +6642,7 @@ dependencies = [
[[package]]
name = "nym-node-tester-wasm"
version = "1.3.1"
version = "1.3.0-rc.0"
dependencies = [
"futures",
"js-sys",
@@ -6806,7 +6798,6 @@ dependencies = [
"tokio",
"tokio-util",
"tracing",
"typed-builder",
"url",
]
@@ -6939,7 +6930,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.65"
version = "1.1.64"
dependencies = [
"bs58",
"clap",
@@ -7386,14 +7377,12 @@ dependencies = [
"jwt-simple",
"nym-crypto",
"nym-http-api-client",
"nym-test-utils",
"reqwest 0.12.22",
"serde",
"serde_json",
"thiserror 2.0.12",
"time",
"tracing",
"utoipa",
]
[[package]]
@@ -7680,7 +7669,7 @@ dependencies = [
[[package]]
name = "nymvisor"
version = "0.1.30"
version = "0.1.29"
dependencies = [
"anyhow",
"bytes",
@@ -7834,9 +7823,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.110"
version = "0.9.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
dependencies = [
"cc",
"libc",
@@ -11402,26 +11391,6 @@ dependencies = [
"utf-8",
]
[[package]]
name = "typed-builder"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d0dd654273fc253fde1df4172c31fb6615cf8b041d3a4008a028ef8b1119e66"
dependencies = [
"typed-builder-macro",
]
[[package]]
name = "typed-builder-macro"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016c26257f448222014296978b2c8456e2cad4de308c35bdb1e383acd569ef5b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "typenum"
version = "1.18.0"
+13 -7
View File
@@ -150,7 +150,7 @@ members = [
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
"tools/internal/mixnet-connectivity-check",
# "tools/internal/sdk-version-bump",
# "tools/internal/sdk-version-bump",
"tools/internal/ssl-inject",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
@@ -215,6 +215,7 @@ base64 = "0.22.1"
base85rs = "0.1.3"
bincode = "1.3.3"
bip39 = { version = "2.0.0", features = ["zeroize"] }
bit-vec = "0.7.0" # can we unify those?
bitvec = "1.0.0"
blake3 = "1.7.0"
bloomfilter = "3.0.1"
@@ -242,11 +243,13 @@ criterion = "0.5"
csv = "1.3.1"
ctr = "0.9.1"
cupid = "0.6.1"
curve25519-dalek = "4.1"
dashmap = "5.5.3"
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
digest = "0.10.7"
dirs = "6.0"
doc-comment = "0.3"
dotenvy = "0.15.6"
dyn-clone = "1.0.19"
ecdsa = "0.16"
@@ -262,8 +265,11 @@ futures = "0.3.31"
futures-util = "0.3"
generic-array = "0.14.7"
getrandom = "0.2.10"
getset = "0.1.5"
handlebars = "3.5.5"
headers = "0.4.0"
hex = "0.4.3"
hex-literal = "0.3.3"
hickory-resolver = "0.25"
hkdf = "0.12.3"
hmac = "0.12.1"
@@ -287,10 +293,12 @@ lazy_static = "1.5.0"
ledger-transport = "0.10.0"
ledger-transport-hid = "0.10.0"
log = "0.4"
maxminddb = "0.23.0"
mime = "0.3.17"
moka = { version = "0.12", features = ["future"] }
nix = "0.27.1"
notify = "5.1.0"
okapi = "0.7.0"
once_cell = "1.21.3"
opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0"
@@ -299,6 +307,7 @@ pem = "0.8"
petgraph = "0.6.5"
pin-project = "1.1"
pnet_packet = "0.35.0"
pin-project-lite = "0.2.16"
publicsuffix = "2.3.0"
proc_pidinfo = "0.1.3"
quote = "1"
@@ -306,10 +315,13 @@ rand = "0.8.5"
rand_chacha = "0.3"
rand_core = "0.6.3"
rand_distr = "0.4"
rand_pcg = "0.3.1"
rand_seeder = "0.2.3"
rayon = "1.5.1"
regex = "1.10.6"
reqwest = { version = "0.12.15", default-features = false }
rs_merkle = "1.5.0"
safer-ffi = "0.1.13"
schemars = "0.8.22"
semver = "1.0.26"
serde = "1.0.219"
@@ -356,7 +368,6 @@ tracing-indicatif = "0.3.9"
tracing-test = "0.2.5"
ts-rs = "10.1.0"
tungstenite = { version = "0.20.1", default-features = false }
typed-builder = "0.23.0"
uniffi = "0.29.2"
uniffi_build = "0.29.0"
url = "2.5"
@@ -453,11 +464,6 @@ opt-level = 'z'
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] }
[workspace.lints.clippy]
suspicious = "deny"
complexity = "deny"
perf = "deny"
style = "deny"
unwrap_used = "deny"
expect_used = "deny"
todo = "deny"
+4 -4
View File
@@ -107,16 +107,16 @@ sdk-wasm-build:
$(MAKE) -C nym-browser-extension/storage wasm-pack
$(MAKE) -C wasm/client
$(MAKE) -C wasm/node-tester
$(MAKE) -C wasm/mix-fetch
# $(MAKE) -C wasm/mix-fetch
$(MAKE) -C wasm/zknym-lib
# $(MAKE) -C wasm/full-nym-wasm
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
sdk-typescript-build:
npx lerna run --scope @nymproject/sdk build --stream
npx lerna run --scope @nymproject/mix-fetch build --stream
npx lerna run --scope @nymproject/node-tester build --stream
yarn --cwd sdk/typescript/codegen/contract-clients build
# npx lerna run --scope @nymproject/mix-fetch build --stream
# npx lerna run --scope @nymproject/node-tester build --stream
# yarn --cwd sdk/typescript/codegen/contract-clients build
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.65"
version = "1.1.64"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.65"
version = "1.1.64"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
+1 -1
View File
@@ -36,7 +36,7 @@ nym-bandwidth-controller = { path = "../bandwidth-controller" }
nym-crypto = { path = "../crypto" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
nym-gateway-requests = { path = "../gateway-requests" }
nym-http-api-client = { path = "../http-api-client", features = ["network-defaults"] }
nym-http-api-client = { path = "../http-api-client" }
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" }
nym-statistics-common = { path = "../statistics" }
+16 -134
View File
@@ -73,10 +73,6 @@ use url::Url;
#[cfg(debug_assertions)]
use wasm_utils::console_log;
/// Default number of retries for Nym API requests when using network details with domain fronting.
/// This allows the client to try alternative URLs if the primary endpoint is unavailable.
const DEFAULT_NYM_API_RETRIES: usize = 3;
#[cfg(all(
not(target_arch = "wasm32"),
feature = "fs-surb-storage",
@@ -216,9 +212,6 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
client_store: S,
dkg_query_client: Option<C>,
// Optional API URLs for domain fronting support
nym_api_urls: Option<Vec<nym_network_defaults::ApiUrl>>,
wait_for_gateway: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
@@ -248,7 +241,6 @@ where
config: base_config,
client_store,
dkg_query_client,
nym_api_urls: None,
wait_for_gateway: false,
custom_topology_provider: None,
custom_gateway_transceiver: None,
@@ -271,16 +263,6 @@ where
self
}
/// Set Nym API URLs for domain fronting support.
///
/// When provided, the client will use these API URLs (which include front_hosts)
/// to construct HTTP clients with domain fronting enabled.
#[must_use]
pub fn with_nym_api_urls(mut self, nym_api_urls: Vec<nym_network_defaults::ApiUrl>) -> Self {
self.nym_api_urls = Some(nym_api_urls);
self
}
#[must_use]
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
self.config.debug.forget_me = *forget_me;
@@ -801,7 +783,7 @@ where
event_tx,
);
let mix_tx = mix_traffic_controller.mix_tx();
let mix_tx = mix_traffic_controller.mix_rx();
let client_tx = mix_traffic_controller.client_tx();
shutdown_tracker.try_spawn_named(
@@ -881,67 +863,21 @@ where
}
fn construct_nym_api_client(
nym_api_urls: Option<&Vec<nym_network_defaults::ApiUrl>>,
config: &Config,
user_agent: Option<UserAgent>,
) -> Result<nym_http_api_client::Client, ClientCoreError> {
tracing::debug!(
"construct_nym_api_client called with nym_api_urls: {}",
nym_api_urls.is_some()
);
// If API URLs are provided, use new_with_fronted_urls() which handles domain fronting
if let Some(nym_api_urls) = nym_api_urls {
if nym_api_urls.is_empty() {
tracing::warn!("Provided nym_api_urls is empty, falling back to config endpoints");
} else {
tracing::info!(
"Building nym-api client from provided URLs (with domain fronting support): {} URLs",
nym_api_urls.len()
);
let mut builder =
nym_http_api_client::ClientBuilder::new_with_fronted_urls(nym_api_urls.clone())
.map_err(ClientCoreError::from)?
.with_retries(DEFAULT_NYM_API_RETRIES);
if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent);
}
return builder.build().map_err(ClientCoreError::from);
}
}
// Fallback to basic client for backwards compatibility
tracing::debug!("Building basic nym-api HTTP client from config endpoints");
let mut nym_api_urls = config.get_nym_api_endpoints();
if nym_api_urls.is_empty() {
tracing::warn!("No API endpoints configured in config, this may cause issues");
}
nym_api_urls.shuffle(&mut thread_rng());
// Convert config URLs to ApiUrl format for consistency
let api_urls: Vec<nym_network_defaults::ApiUrl> = nym_api_urls
.into_iter()
.map(|url| nym_network_defaults::ApiUrl {
url: url.to_string(),
front_hosts: None,
})
.collect();
tracing::debug!("Using {} config API endpoints", api_urls.len());
let mut builder = nym_http_api_client::ClientBuilder::new_with_fronted_urls(api_urls)
.map_err(ClientCoreError::from)?
.with_retries(DEFAULT_NYM_API_RETRIES)
.with_bincode();
let mut builder = nym_http_api_client::Client::builder(nym_api_urls[0].clone())
.map_err(ClientCoreError::from)?;
if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent);
}
builder = builder.with_bincode();
builder.build().map_err(ClientCoreError::from)
}
@@ -1004,8 +940,8 @@ where
// Create a shutdown tracker for this client - either as a child of provided tracker
// or get one from the registry
let shutdown_tracker = match self.shutdown {
Some(parent_tracker) => parent_tracker.clone(),
None => nym_task::create_sdk_shutdown_tracker()?,
Some(parent_tracker) => parent_tracker.child_tracker(),
None => nym_task::get_sdk_shutdown_tracker()?,
};
Self::start_event_control(self.event_tx, event_receiver, &shutdown_tracker);
@@ -1025,11 +961,7 @@ where
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
let nym_api_client = Self::construct_nym_api_client(
self.nym_api_urls.as_ref(),
&self.config,
self.user_agent.clone(),
)?;
let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone())?;
let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?;
let topology_provider = Self::setup_topology_provider(
@@ -1044,7 +976,7 @@ where
self.user_agent.clone(),
generate_client_stats_id(*self_address.identity()),
input_sender.clone(),
&shutdown_tracker.clone(),
&shutdown_tracker.child_tracker(),
);
// needs to be started as the first thing to block if required waiting for the gateway
@@ -1054,7 +986,7 @@ where
shared_topology_accessor.clone(),
self_address.gateway(),
self.wait_for_gateway,
&shutdown_tracker.clone(),
&shutdown_tracker.child_tracker(),
)
.await?;
@@ -1074,7 +1006,7 @@ where
stats_reporter.clone(),
#[cfg(unix)]
self.connection_fd_callback,
&shutdown_tracker.clone(),
&shutdown_tracker.child_tracker(),
)
.await?;
let gateway_ws_fd = gateway_transceiver.ws_fd();
@@ -1082,7 +1014,7 @@ where
let reply_storage = Self::setup_persistent_reply_storage(
reply_storage_backend,
key_rotation_config,
&shutdown_tracker.clone(),
&shutdown_tracker.child_tracker(),
)
.await?;
@@ -1093,7 +1025,7 @@ where
reply_storage.key_storage(),
reply_controller_sender.clone(),
stats_reporter.clone(),
&shutdown_tracker.clone(),
&shutdown_tracker.child_tracker(),
);
// The message_sender is the transmitter for any component generating sphinx packets
@@ -1103,7 +1035,7 @@ where
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
gateway_transceiver,
&shutdown_tracker.clone(),
&shutdown_tracker.child_tracker(),
EventSender(event_sender),
);
@@ -1134,7 +1066,7 @@ where
shared_lane_queue_lengths.clone(),
client_connection_rx,
stats_reporter.clone(),
&shutdown_tracker.clone(),
&shutdown_tracker.child_tracker(),
);
if !self
@@ -1150,7 +1082,7 @@ where
shared_topology_accessor.clone(),
message_sender,
stats_reporter.clone(),
&shutdown_tracker.clone(),
&shutdown_tracker.child_tracker(),
);
}
@@ -1204,53 +1136,3 @@ pub struct BaseClient {
pub forget_me: ForgetMe,
pub remember_me: RememberMe,
}
#[cfg(test)]
mod tests {
use super::*;
use nym_network_defaults::{ApiUrl, NymNetworkDetails};
#[test]
fn test_network_details_with_multiple_urls() {
// Verify that network details can be configured with multiple API URLs
let mut network_details = NymNetworkDetails::new_empty();
network_details.nym_api_urls = Some(vec![
ApiUrl {
url: "https://validator.nymtech.net/api/".to_string(),
front_hosts: None,
},
ApiUrl {
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
},
]);
assert_eq!(network_details.nym_api_urls.as_ref().unwrap().len(), 2);
assert!(network_details.nym_api_urls.as_ref().unwrap()[1]
.front_hosts
.is_some());
}
#[test]
fn test_network_details_with_front_hosts() {
// Verify that ApiUrl can store domain fronting configuration
let api_url = ApiUrl {
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
};
assert_eq!(api_url.url, "https://nym-frontdoor.vercel.app/api/");
assert_eq!(api_url.front_hosts.as_ref().unwrap().len(), 2);
assert!(api_url
.front_hosts
.as_ref()
.unwrap()
.contains(&"vercel.app".to_string()));
}
#[test]
fn test_default_nym_api_retries_constant() {
// Verify the retry constant is set correctly
assert_eq!(DEFAULT_NYM_API_RETRIES, 3);
}
}
@@ -205,7 +205,7 @@ impl LoopCoverTrafficStream<OsRng> {
TrySendError::Full(_) => {
// This isn't a problem, if the channel is full means we're already sending the
// max amount of messages downstream can handle.
tracing::trace!("Failed to send cover message - channel full");
tracing::debug!("Failed to send cover message - channel full");
}
TrySendError::Closed(_) => {
tracing::warn!("Failed to send cover message - channel closed");
@@ -20,10 +20,7 @@ pub mod transceiver;
// We remind ourselves that 32 x 32kb = 1024kb, a reasonable size for a network buffer.
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
/// Reduced from 100 to 20 to fail fast (~1-2 seconds instead of ~6 seconds).
/// If we can't send 20 packets in a row, the gateway is unreachable.
const MAX_FAILURE_COUNT: usize = 20;
const MAX_FAILURE_COUNT: usize = 100;
// that's also disgusting.
pub struct Empty;
@@ -87,7 +84,7 @@ impl MixTrafficController {
self.client_tx.clone()
}
pub fn mix_tx(&self) -> BatchMixMessageSender {
pub fn mix_rx(&self) -> BatchMixMessageSender {
self.mix_tx.clone()
}
@@ -159,11 +156,6 @@ impl MixTrafficController {
// Do we need to handle the embedded mixnet client case
// separately?
self.event_tx.send(MixnetClientEvent::Traffic(MixTrafficEvent::FailedSendingSphinx));
// IMO it shouldn't be signalled from there but it is how it is
// TODO : report the failure upwards and shutdown from upwards
// Gateway is dead, we have to shut down currently
error!("Signalling shutdown from the MixTrafficController");
self.shutdown_token.cancel();
break;
}
}
@@ -298,8 +298,6 @@ where
"failed to send mixnet packet due to closed channel (outside of shutdown!)"
);
}
// Early return to avoid further processing when channel is closed
return;
}
Ok(_) => {
let event = if fragment_id.is_some() {
+2 -2
View File
@@ -151,7 +151,7 @@ pub async fn gateways_for_init(
}
let retry_count = retry_count.unwrap_or(DEFAULT_NYM_API_RETRIES);
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone())?
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone())
.with_retries(retry_count)
.with_bincode();
@@ -441,7 +441,7 @@ mod tests {
#[test]
fn test_multiple_urls_prepared_for_retries() {
let urls = [
let urls = vec![
Url::parse("https://api1.nym.com").unwrap(),
Url::parse("https://api2.nym.com").unwrap(),
Url::parse("https://api3.nym.com").unwrap(),
@@ -241,28 +241,23 @@ impl Epoch {
//
// Note: It's important that the variant ordering is not changed otherwise it would mess up the derived `PartialOrd`
#[cw_serde]
#[derive(Copy, Default)]
#[derive(Copy)]
pub enum EpochState {
#[default]
WaitingInitialisation,
PublicKeySubmission {
resharing: bool,
},
DealingExchange {
resharing: bool,
},
VerificationKeySubmission {
resharing: bool,
},
VerificationKeyValidation {
resharing: bool,
},
VerificationKeyFinalization {
resharing: bool,
},
PublicKeySubmission { resharing: bool },
DealingExchange { resharing: bool },
VerificationKeySubmission { resharing: bool },
VerificationKeyValidation { resharing: bool },
VerificationKeyFinalization { resharing: bool },
InProgress,
}
impl Default for EpochState {
fn default() -> Self {
Self::WaitingInitialisation
}
}
impl Display for EpochState {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
+1 -1
View File
@@ -8,7 +8,7 @@ async fn main() -> anyhow::Result<()> {
use sqlx::{Connection, SqliteConnection};
use std::env;
let out_dir = env::var("OUT_DIR").context("missing OUT_DIR env variable")?;
let out_dir = env::var("OUT_DIR")?;
let database_path = format!("{out_dir}/nym-credential-proxy-example.sqlite");
// remove the db file if it already existed from previous build
-7
View File
@@ -127,13 +127,6 @@ pub enum CredentialProxyError {
#[error("failed to create deposit")]
DepositFailure,
#[error("failed to load jwt signing key from {path}: {err}")]
JWTSigningKeyLoadFailure {
path: String,
#[source]
err: std::io::Error,
},
#[error("can't obtain sufficient number of credential shares due to unavailable quorum")]
UnavailableSigningQuorum,
@@ -7,9 +7,9 @@ use crate::ticketbook_manager::TicketbookManager;
use nym_compact_ecash::Base58;
use nym_credential_proxy_requests::api::v1::ticketbook::models::{
CurrentEpochResponse, DepositResponse, GlobalDataParams, MasterVerificationKeyResponse,
ObtainTicketBookSharesAsyncResponse, PartialVerificationKey, PartialVerificationKeysResponse,
TicketbookAsyncRequest, TicketbookObtainParams, TicketbookRequest,
TicketbookWalletSharesAsyncResponse, TicketbookWalletSharesResponse,
PartialVerificationKey, PartialVerificationKeysResponse, TicketbookAsyncRequest,
TicketbookObtainParams, TicketbookRequest, TicketbookWalletSharesAsyncResponse,
TicketbookWalletSharesResponse,
};
use time::OffsetDateTime;
use tracing::{Instrument, Level, error, info, span, warn};
@@ -65,7 +65,7 @@ impl TicketbookManager {
uuid: Uuid,
request: TicketbookAsyncRequest,
params: TicketbookObtainParams,
) -> Result<ObtainTicketBookSharesAsyncResponse, CredentialProxyError> {
) -> Result<TicketbookWalletSharesAsyncResponse, CredentialProxyError> {
let requested_on = OffsetDateTime::now_utc();
let span = span!(Level::INFO, "[async] obtain ticketboook", uuid = %uuid);
async move {
@@ -110,7 +110,7 @@ impl TicketbookManager {
}
// 4. in the meantime, return the id to the user
Ok(TicketbookWalletSharesAsyncResponse { id, uuid }.into())
Ok(TicketbookWalletSharesAsyncResponse { id, uuid })
}
.instrument(span)
.await
@@ -246,7 +246,7 @@ mod tests {
let _exp_date_sigs = generate_expiration_date_signatures(
sig_req.expiration_date.ecash_unix_timestamp(),
&[signing_keys.secret_key()],
&[signing_keys.verification_key()],
&vec![signing_keys.verification_key()],
&signing_keys.verification_key(),
&[1],
)?;
@@ -263,7 +263,7 @@ mod tests {
let wallet = issuance.aggregate_signature_shares(
&signing_keys.verification_key(),
&[partial_wallet],
&vec![partial_wallet],
sig_req,
)?;
-4
View File
@@ -36,11 +36,7 @@ nym-sphinx-types = { path = "../nymsphinx/types", version = "0.2.0", default-fea
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
[dev-dependencies]
anyhow = { workspace = true }
rand_chacha = { workspace = true }
serde_json = { workspace = true }
nym-test-utils = { path = "../test-utils" }
[features]
default = []
@@ -17,51 +17,6 @@ pub mod bs58_ed25519_pubkey {
}
}
pub mod vec_bs58_ed25519_pubkey {
use super::*;
use serde::{Deserialize, Deserializer, Serializer, ser::SerializeSeq};
pub fn serialize<S: Serializer>(
keys: &Vec<PublicKey>,
serializer: S,
) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(keys.len()))?;
for key in keys {
seq.serialize_element(&Bs58KeyWrapper(*key))?;
}
seq.end()
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<PublicKey>, D::Error> {
let wrapped = Vec::<Bs58KeyWrapper>::deserialize(deserializer)?;
Ok(wrapped.into_iter().map(|k| k.0).collect())
}
struct Bs58KeyWrapper(PublicKey);
impl serde::Serialize for Bs58KeyWrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
bs58_ed25519_pubkey::serialize(&self.0, serializer)
}
}
impl<'de> Deserialize<'de> for Bs58KeyWrapper {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Bs58KeyWrapper(bs58_ed25519_pubkey::deserialize(
deserializer,
)?))
}
}
}
pub mod bs58_ed25519_signature {
use crate::asymmetric::ed25519::Signature;
use serde::{Deserialize, Deserializer, Serializer};
@@ -78,53 +33,3 @@ pub mod bs58_ed25519_signature {
Signature::from_base58_string(s).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use jwt_simple::reexports::{anyhow, serde_json};
use nym_test_utils::helpers::deterministic_rng;
use serde::{Deserialize, Serialize};
#[test]
fn vec_bs58_ed25519_pubkey_json() -> anyhow::Result<()> {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct KeysWrapper(#[serde(with = "vec_bs58_ed25519_pubkey")] Vec<PublicKey>);
use crate::asymmetric::ed25519;
let mut rng = deterministic_rng();
let empty = KeysWrapper(vec![]);
let single_key = KeysWrapper(vec![PublicKey::from_base58_string(
"Be9wH7xuXBRJAuV1pC7MALZv6a61RvWQ3SypsNarqTt",
)?]);
let three_keys = KeysWrapper(vec![
ed25519::KeyPair::new(&mut rng).public_key,
ed25519::KeyPair::new(&mut rng).public_key,
ed25519::KeyPair::new(&mut rng).public_key,
]);
let se_empty = serde_json::to_string(&empty)?;
let se_single_key = serde_json::to_string(&single_key)?;
let se_three_keys = serde_json::to_string(&three_keys)?;
assert_eq!(se_empty, r#"[]"#);
assert_eq!(
se_single_key,
r#"["Be9wH7xuXBRJAuV1pC7MALZv6a61RvWQ3SypsNarqTt"]"#
);
assert_eq!(
se_three_keys,
r#"["HmgHDV79LpnEaSUp8QZQwSroxVvS4RewF7yM9e7qu8y3","311xRh859qCd5MVqoPRCoNx26eYhLknGwtjzkkTJFGhf","A5BMp8WJ6Uk91U4JpWRv2Bc6X35AaRaSEy8QEWeAkaBv"]"#
);
let empty_de = serde_json::from_str::<KeysWrapper>(&se_empty)?;
let single_key_de = serde_json::from_str::<KeysWrapper>(&se_single_key)?;
let three_keys_de = serde_json::from_str::<KeysWrapper>(&se_three_keys)?;
assert_eq!(empty, empty_de);
assert_eq!(single_key, single_key_de);
assert_eq!(three_keys, three_keys_de);
Ok(())
}
}
+3 -3
View File
@@ -82,7 +82,7 @@ mod tests {
let exp_date_sigs = generate_expiration_date_signatures(
sig_req.expiration_date.ecash_unix_timestamp(),
&[keypair.secret_key()],
&[keypair.verification_key()],
&vec![keypair.verification_key()],
&keypair.verification_key(),
&[keypair.index.unwrap()],
)
@@ -106,14 +106,14 @@ mod tests {
.unwrap();
let wallet = issuance
.aggregate_signature_shares(&keypair.verification_key(), &[partial_wallet], sig_req)
.aggregate_signature_shares(&keypair.verification_key(), &vec![partial_wallet], sig_req)
.unwrap();
let mut issued = issuance.into_issued_ticketbook(wallet, 1);
let coin_indices_signatures = generate_coin_indices_signatures(
nym_credentials_interface::ecash_parameters(),
&[keypair.secret_key()],
&[keypair.verification_key()],
&vec![keypair.verification_key()],
&keypair.verification_key(),
&[keypair.index.unwrap()],
)
+20 -38
View File
@@ -296,9 +296,6 @@ impl std::error::Error for ReqwestErrorWrapper {}
#[derive(Debug, Error)]
#[allow(missing_docs)]
pub enum HttpClientError {
#[error("did not provide any valid client URLs")]
NoUrlsProvided,
#[error("failed to construct inner reqwest client: {source}")]
ReqwestBuildError {
#[source]
@@ -585,29 +582,24 @@ impl ClientBuilder {
Self::new(alt)
} else {
let url = url.to_url()?;
Self::new_with_urls(vec![url])
Ok(Self::new_with_urls(vec![url]))
}
}
/// Create a client builder from network details with sensible defaults
#[cfg(feature = "network-defaults")]
// deprecating function since it's not clear from its signature whether the client
// would be constructed using `nym_api_urls` or `nym_vpn_api_urls`
#[deprecated(note = "use explicit Self::new_with_fronted_urls instead")]
pub fn from_network(
network: &nym_network_defaults::NymNetworkDetails,
) -> Result<Self, HttpClientError> {
let urls = network.nym_api_urls.as_ref().cloned().unwrap_or_default();
Self::new_with_fronted_urls(urls.clone())
}
/// Create a client builder using the provided set of domain-fronted URLs
#[cfg(feature = "network-defaults")]
pub fn new_with_fronted_urls(
urls: Vec<nym_network_defaults::ApiUrl>,
) -> Result<Self, HttpClientError> {
let urls = urls
.into_iter()
let urls = network
.nym_api_urls
.as_ref()
.ok_or_else(|| {
HttpClientError::GenericRequestFailure(
"No API URLs configured in network details".to_string(),
)
})?
.iter()
.map(|api_url| {
// Convert ApiUrl to our Url type with fronting support
let mut url = Url::parse(&api_url.url)?;
@@ -619,19 +611,15 @@ impl ClientBuilder {
.iter()
.map(|host| format!("https://{}", host))
.collect();
url = Url::new(api_url.url.clone(), Some(fronts)).map_err(|source| {
HttpClientError::MalformedUrl {
raw: api_url.url.clone(),
source,
}
})?;
url = Url::new(api_url.url.clone(), Some(fronts))
.map_err(|e| HttpClientError::GenericRequestFailure(e.to_string()))?;
}
Ok(url)
})
.collect::<Result<Vec<_>, HttpClientError>>()?;
let mut builder = Self::new_with_urls(urls)?;
let mut builder = Self::new_with_urls(urls);
// Enable domain fronting by default (on retry)
#[cfg(feature = "tunneling")]
@@ -643,11 +631,7 @@ impl ClientBuilder {
}
/// Constructs a new http `ClientBuilder` from a valid url.
pub fn new_with_urls(urls: Vec<Url>) -> Result<Self, HttpClientError> {
if urls.is_empty() {
return Err(HttpClientError::NoUrlsProvided);
}
pub fn new_with_urls(urls: Vec<Url>) -> Self {
let urls = Self::check_urls(urls);
#[cfg(target_arch = "wasm32")]
@@ -656,7 +640,7 @@ impl ClientBuilder {
#[cfg(not(target_arch = "wasm32"))]
let reqwest_client_builder = default_builder();
Ok(ClientBuilder {
ClientBuilder {
urls,
timeout: None,
custom_user_agent: false,
@@ -667,7 +651,7 @@ impl ClientBuilder {
retry_limit: 0,
serialization: SerializationFormat::Json,
})
}
}
/// Add an additional URL to the set usable by this constructed `Client`
@@ -902,8 +886,6 @@ impl Client {
if self.base_urls.len() > 1 {
let orig = self.current_idx.load(Ordering::Relaxed);
#[allow(unused_mut)]
let mut next = (orig + 1) % self.base_urls.len();
// if fronting is enabled we want to update to a host that has fronts configured
@@ -966,13 +948,13 @@ impl Client {
return (url.as_str(), url.front_str());
} else {
tracing::debug!(
"Domain fronting is enabled, but no host_url is defined for current URL"
warn!(
"Domain fronting is enabled, but no host_url is defined! Domain fronting WILL NOT WORK"
)
}
} else {
tracing::debug!(
"Domain fronting is enabled, but current URL has no front_hosts configured"
warn!(
"Domain fronting is enabled, but no front_url is defined! Domain fronting WILL NOT WORK"
)
}
}
+1 -9
View File
@@ -21,10 +21,6 @@ inventory::collect!(ConfigRecord);
/// Returns the default builder with all registered configurations applied.
pub fn default_builder() -> ReqwestClientBuilder {
let mut b = ReqwestClientBuilder::new();
#[cfg(feature = "debug-inventory")]
let mut test_client = ReqwestClientBuilder::new();
let mut records: Vec<&'static ConfigRecord> =
inventory::iter::<ConfigRecord>.into_iter().collect();
records.sort_by_key(|r| r.priority); // lower runs first
@@ -39,10 +35,6 @@ pub fn default_builder() -> ReqwestClientBuilder {
for r in records {
b = (r.apply)(b);
#[cfg(feature = "debug-inventory")]
{
test_client = (r.apply)(test_client);
}
}
#[cfg(feature = "debug-inventory")]
@@ -55,7 +47,7 @@ pub fn default_builder() -> ReqwestClientBuilder {
eprintln!("[HTTP-INVENTORY] Building test client to verify configuration...");
// Try to build a client to see if it works
match test_client.build() {
match b.try_clone().unwrap().build() {
Ok(client) => {
eprintln!("[HTTP-INVENTORY] ✓ Client built successfully");
eprintln!("[HTTP-INVENTORY] Client debug info: {:#?}", client);
+49 -110
View File
@@ -2,77 +2,77 @@ use super::*;
#[test]
fn sanitizing_urls() {
let base_url: Url = "http://api.test".parse().unwrap();
let base_url: Url = "http://foomp.com".parse().unwrap();
// works with a full string
assert_eq!(
"http://api.test/foo/bar",
"http://foomp.com/foo/bar",
sanitize_url(&base_url, "/foo//bar/", NO_PARAMS).as_str()
);
// (and leading slash doesn't matter)
assert_eq!(
"http://api.test/foo/bar",
"http://foomp.com/foo/bar",
sanitize_url(&base_url, "foo//bar/", NO_PARAMS).as_str()
);
// works with 1 segment
assert_eq!(
"http://api.test/foo",
"http://foomp.com/foo",
sanitize_url(&base_url, &["foo"], NO_PARAMS).as_str()
);
// works with 2 segments
assert_eq!(
"http://api.test/foo/bar",
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
);
// works with leading slash
assert_eq!(
"http://api.test/foo",
"http://foomp.com/foo",
sanitize_url(&base_url, &["/foo"], NO_PARAMS).as_str()
);
assert_eq!(
"http://api.test/foo/bar",
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://api.test/foo/bar",
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
);
// works with trailing slash
assert_eq!(
"http://api.test/foo",
"http://foomp.com/foo",
sanitize_url(&base_url, &["foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://api.test/foo/bar",
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
);
assert_eq!(
"http://api.test/foo/bar",
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
);
// works with both leading and trailing slash
assert_eq!(
"http://api.test/foo",
"http://foomp.com/foo",
sanitize_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
);
assert_eq!(
"http://api.test/foo/bar",
"http://foomp.com/foo/bar",
sanitize_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
);
// adds params
assert_eq!(
"http://api.test/foo/bar?foomp=baz",
"http://foomp.com/foo/bar?foomp=baz",
sanitize_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
);
assert_eq!(
"http://api.test/foo/bar?arg1=val1&arg2=val2",
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
sanitize_url(
&base_url,
&["/foo/", "/bar/"],
@@ -91,87 +91,83 @@ fn sanitizing_urls() {
#[tokio::test]
async fn api_client_retry() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::new_with_urls(vec![
"http://broken.nym.test".parse()?, // This will fail
"https://httpbin.org/status/200".parse()?, // This will succeed
])?
"http://broken.nym.badurl".parse()?,
"http://example.com/".parse()?,
])
.with_retries(3)
.build()?;
let req = client.create_get_request(&[], NO_PARAMS).unwrap();
let req = client.create_get_request(&["/"], NO_PARAMS).unwrap();
let resp = client.send(req).await?;
// The main test is that we successfully retried and switched to the working URL
// We accept any response from the working endpoint since external services can be unreliable
assert_eq!(
client.current_url().as_str(),
"https://httpbin.org/status/200"
);
assert_eq!(resp.status(), 200);
println!("Response status: {}", resp.status());
// check that the url was updated
assert_eq!(client.current_url().as_str(), "http://example.com/");
Ok(())
}
#[test]
fn host_updating() {
let url = Url::new("http://nym-api1.test", None).unwrap();
let url = Url::new("http://example.com", None).unwrap();
let mut client = ClientBuilder::new(url).unwrap().build().unwrap();
// check that the url is set correctly
let current_url = client.current_url();
assert_eq!(current_url.as_str(), "http://nym-api1.test/");
assert_eq!(current_url.as_str(), "http://example.com/");
assert_eq!(current_url.front_str(), None);
// update the url
client.update_host();
// check that the url is still the same since there is one URL
assert_eq!(client.current_url().as_str(), "http://nym-api1.test/");
assert_eq!(client.current_url().as_str(), "http://example.com/");
// =======================================
// we rotate through urls when available
let new_urls = vec![
Url::new("http://nym-api1.test", None).unwrap(),
Url::new("http://nym-api2.test", None).unwrap(),
Url::new("http://example.com", None).unwrap(),
Url::new("http://example.org", None).unwrap(),
];
client.change_base_urls(new_urls);
assert_eq!(client.current_url().as_str(), "http://nym-api1.test/");
assert_eq!(client.current_url().as_str(), "http://example.com/");
client.update_host();
// check that the url got updated now that there are multiple URLs
assert_eq!(client.current_url().as_str(), "http://nym-api2.test/");
assert_eq!(client.current_url().as_str(), "http://example.org/");
assert_eq!(client.current_url().front_str(), None);
client.update_host();
assert_eq!(client.current_url().as_str(), "http://nym-api1.test/");
assert_eq!(client.current_url().as_str(), "http://example.com/");
// =======================================
// we rotate through urls when available if fronting is disabled
let new_urls = vec![
Url::new(
"http://nym-api1.test",
Some(vec!["http://cdn1.test", "http://cdn2.test"]),
"http://example.com",
Some(vec!["http://front1.com", "http://front2.com"]),
)
.unwrap(),
Url::new("http://nym-api2.test", None).unwrap(),
Url::new("http://example.org", None).unwrap(),
];
client.change_base_urls(new_urls);
assert_eq!(client.current_url().as_str(), "http://nym-api1.test/");
assert_eq!(client.current_url().as_str(), "http://example.com/");
client.update_host();
// check that the url got updated now that there are multiple URLs
assert_eq!(client.current_url().as_str(), "http://nym-api2.test/");
assert_eq!(client.current_url().as_str(), "http://example.org/");
}
#[test]
#[cfg(feature = "tunneling")]
fn fronted_host_updating() {
let url = Url::new("http://nym-api.test", Some(vec!["http://cdn1.test"])).unwrap();
let url = Url::new("http://example.com", Some(vec!["http://front1.com"])).unwrap();
let mut client = ClientBuilder::new(url)
.unwrap()
.with_fronting(crate::fronted::FrontPolicy::Always)
@@ -180,103 +176,46 @@ fn fronted_host_updating() {
// check that the url is set correctly
let current_url = client.current_url();
assert_eq!(current_url.as_str(), "http://nym-api.test/");
assert_eq!(current_url.front_str(), Some("cdn1.test"));
assert_eq!(current_url.as_str(), "http://example.com/");
assert_eq!(current_url.front_str(), Some("front1.com"));
// update the url
client.update_host();
// check that the url is still the same since there is one URL and one front
let current_url = client.current_url();
assert_eq!(current_url.as_str(), "http://nym-api.test/");
assert_eq!(current_url.front_str(), Some("cdn1.test"));
assert_eq!(current_url.as_str(), "http://example.com/");
assert_eq!(current_url.front_str(), Some("front1.com"));
// =======================================
// we rotate through front urls when available if fronting is enabled
let new_urls = vec![
Url::new(
"http://nym-api.test",
Some(vec!["http://cdn1.test", "http://cdn2.test"]),
"http://example.com",
Some(vec!["http://front1.com", "http://front2.com"]),
)
.unwrap(),
Url::new("http://nym-api2.test", None).unwrap(),
Url::new("http://example.org", None).unwrap(),
];
client.change_base_urls(new_urls);
let current_url = client.current_url();
assert_eq!(current_url.as_str(), "http://nym-api.test/");
assert_eq!(current_url.front_str(), Some("cdn1.test"));
assert_eq!(current_url.as_str(), "http://example.com/");
assert_eq!(current_url.front_str(), Some("front1.com"));
// update the url - this should keep the same host but change the front
client.update_host();
let current_url = client.current_url();
// check that the url is still the same since there is one URL
assert_eq!(current_url.as_str(), "http://nym-api.test/");
assert_eq!(current_url.front_str(), Some("cdn2.test"));
assert_eq!(current_url.as_str(), "http://example.com/");
assert_eq!(current_url.front_str(), Some("front2.com"));
// update the url - this should wrap around to the first front as the second url is not fronted
client.update_host();
let current_url = client.current_url();
assert_eq!(current_url.as_str(), "http://nym-api.test/");
assert_eq!(current_url.front_str(), Some("cdn1.test"));
}
#[test]
#[cfg(feature = "network-defaults")]
fn from_network_configures_multiple_urls_and_retries() {
use nym_network_defaults::{ApiUrl, NymNetworkDetails};
// Create network details with multiple URLs and fronting
let mut network_details = NymNetworkDetails::new_empty();
network_details.nym_api_urls = Some(vec![
ApiUrl {
url: "https://validator.nymtech.net/api/".to_string(),
front_hosts: None,
},
ApiUrl {
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
},
ApiUrl {
url: "https://nym-frontdoor.global.ssl.fastly.net/api/".to_string(),
front_hosts: Some(vec!["yelp.global.ssl.fastly.net".to_string()]),
},
]);
// Build client from network details
let client = ClientBuilder::new_with_fronted_urls(
network_details.nym_api_urls.clone().unwrap_or_default(),
)
.expect("Failed to create client from network")
.build()
.expect("Failed to build client");
// Verify all URLs were configured
assert_eq!(
client.base_urls().len(),
3,
"Expected 3 URLs to be configured from network details"
);
// Verify the URLs have fronting configured where appropriate
assert_eq!(
client.base_urls()[0].as_str(),
"https://validator.nymtech.net/api/"
);
assert!(client.base_urls()[0].front_str().is_none());
assert_eq!(
client.base_urls()[1].as_str(),
"https://nym-frontdoor.vercel.app/api/"
);
assert!(client.base_urls()[1].front_str().is_some());
assert_eq!(
client.base_urls()[2].as_str(),
"https://nym-frontdoor.global.ssl.fastly.net/api/"
);
assert!(client.base_urls()[2].front_str().is_some());
assert_eq!(current_url.as_str(), "http://example.com/");
assert_eq!(current_url.front_str(), Some("front1.com"));
}
+1 -13
View File
@@ -124,8 +124,6 @@ impl NymNetworkDetails {
}
}
let nym_api = var(var_names::NYM_API).expect("nym api not set");
NymNetworkDetails::new_empty()
.with_network_name(var(var_names::NETWORK_NAME).expect("network name not set"))
.with_bech32_account_prefix(
@@ -151,7 +149,7 @@ impl NymNetworkDetails {
})
.with_additional_validator_endpoint(ValidatorDetails::new(
var(var_names::NYXD).expect("nyxd validator not set"),
Some(nym_api.clone()),
Some(var(var_names::NYM_API).expect("nym api not set")),
get_optional_env(var_names::NYXD_WEBSOCKET),
))
.with_mixnet_contract(get_optional_env(var_names::MIXNET_CONTRACT_ADDRESS))
@@ -161,10 +159,6 @@ impl NymNetworkDetails {
.with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS))
.with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS))
.with_nym_vpn_api_url(get_optional_env(var_names::NYM_VPN_API))
.with_nym_api_urls(Some(vec![ApiUrl {
url: nym_api,
front_hosts: None,
}]))
}
pub fn new_mainnet() -> Self {
@@ -354,12 +348,6 @@ impl NymNetworkDetails {
self
}
#[must_use]
pub fn with_nym_api_urls(mut self, urls: Option<Vec<ApiUrl>>) -> Self {
self.nym_api_urls = urls;
self
}
pub fn nym_vpn_api_url(&self) -> Option<Url> {
self.nym_vpn_api_url.as_ref().map(|url| {
url.parse()
+2 -2
View File
@@ -24,6 +24,6 @@ pub use crate::runtime_registry::RegistryAccessError;
/// Get or create a ShutdownTracker for SDK use.
/// This provides automatic task management without requiring manual setup.
pub fn create_sdk_shutdown_tracker() -> Result<ShutdownTracker, RegistryAccessError> {
Ok(runtime_registry::RuntimeRegistry::create_sdk()?.shutdown_tracker_owned())
pub fn get_sdk_shutdown_tracker() -> Result<ShutdownTracker, RegistryAccessError> {
Ok(runtime_registry::RuntimeRegistry::get_or_create_sdk()?.shutdown_tracker_owned())
}
+16 -34
View File
@@ -19,45 +19,30 @@ pub(crate) struct RuntimeRegistry {
pub enum RegistryAccessError {
#[error("the runtime registry is poisoned")]
Poisoned,
#[error("The SDK ShutdownManager already exists")]
ExistingShutdownManager,
#[error("No existing SDK ShutdownManager")]
MissingShutdownManager,
}
impl RuntimeRegistry {
/// Create a ShutdownManager for SDK use.
/// Get or create a ShutdownManager for SDK use.
/// This manager doesn't listen to OS signals, making it suitable for library use.
/// This function overwrite any existing manager!
pub(crate) fn create_sdk() -> Result<Arc<ShutdownManager>, RegistryAccessError> {
let mut guard = REGISTRY
.sdk_manager
.write()
.map_err(|_| RegistryAccessError::Poisoned)?;
Ok(guard
.insert(Arc::new(
ShutdownManager::new_without_signals().with_cancel_on_panic(),
))
.clone())
}
/// Get the ShutdownManager for SDK use.
/// This manager doesn't listen to OS signals, making it suitable for library use.
/// Not yet used, but maybe in the future
#[allow(dead_code)]
pub(crate) fn get_sdk() -> Result<Arc<ShutdownManager>, RegistryAccessError> {
pub(crate) fn get_or_create_sdk() -> Result<Arc<ShutdownManager>, RegistryAccessError> {
let guard = REGISTRY
.sdk_manager
.read()
.map_err(|_| RegistryAccessError::Poisoned)?;
if let Some(manager) = guard.as_ref() {
Ok(manager.clone())
} else {
Err(RegistryAccessError::MissingShutdownManager)
return Ok(manager.clone());
}
drop(guard);
let mut guard = REGISTRY
.sdk_manager
.write()
.map_err(|_| RegistryAccessError::Poisoned)?;
Ok(guard
.get_or_insert_with(|| {
Arc::new(ShutdownManager::new_without_signals().with_cancel_on_panic())
})
.clone())
}
/// Check if an SDK manager has been created.
@@ -100,13 +85,10 @@ mod tests {
assert!(!RuntimeRegistry::has_sdk_manager().unwrap());
// Error if nothing was created
assert!(RuntimeRegistry::get_sdk().is_err());
let manager1 = RuntimeRegistry::create_sdk().unwrap();
let manager1 = RuntimeRegistry::get_or_create_sdk().unwrap();
assert!(RuntimeRegistry::has_sdk_manager().unwrap());
let manager2 = RuntimeRegistry::get_sdk().unwrap();
let manager2 = RuntimeRegistry::get_or_create_sdk().unwrap();
// Should return the same instance
assert!(Arc::ptr_eq(&manager1, &manager2));
+1 -8
View File
@@ -15,10 +15,9 @@ jwt-simple = { workspace = true }
reqwest = { workspace = true, features = ["rustls-tls"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
time = { workspace = true, features = ["serde", "formatting", "parsing"] }
time = { workspace = true, features = ["serde"] }
thiserror = { workspace = true }
tracing = { workspace = true }
utoipa = { workspace = true, optional = true }
nym-http-api-client = { path = "../http-api-client", default-features = false }
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde", "naive_jwt"] }
@@ -26,12 +25,6 @@ nym-crypto = { path = "../crypto", features = ["asymmetric", "serde", "naive_jwt
[dev-dependencies]
anyhow = { workspace = true }
time = { workspace = true, features = ["macros"] }
nym-test-utils = { path = "../test-utils" }
nym-crypto = { path = "../crypto", features = ["rand"] }
[features]
openapi = ["utoipa"]
[lints]
workspace = true
+16 -46
View File
@@ -1,43 +1,31 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::UpgradeModeCheckError;
use nym_crypto::asymmetric::ed25519;
use nym_http_api_client::generate_user_agent;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use time::OffsetDateTime;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
pub struct UpgradeModeAttestation {
#[serde(flatten)]
pub content: UpgradeModeAttestationContent,
#[serde(with = "ed25519::bs58_ed25519_signature")]
#[cfg_attr(feature = "openapi", schema(value_type = String))]
pub signature: ed25519::Signature,
}
impl UpgradeModeAttestation {
pub fn authorised_to_issue_jwt(&self, key: &ed25519::PublicKey) -> bool {
self.content.authorised_jwt_issuers.contains(key)
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
#[serde(tag = "type")]
#[serde(rename = "upgrade_mode")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct UpgradeModeAttestationContent {
#[serde(with = "time::serde::rfc3339")]
#[cfg_attr(feature = "openapi", schema(value_type = String))]
#[serde(with = "time::serde::timestamp")]
pub starting_time: OffsetDateTime,
#[serde(with = "ed25519::bs58_ed25519_pubkey")]
#[cfg_attr(feature = "openapi", schema(value_type = String))]
pub attester_public_key: ed25519::PublicKey,
#[serde(with = "ed25519::vec_bs58_ed25519_pubkey")]
#[cfg_attr(feature = "openapi", schema(value_type = Vec<String>))]
pub authorised_jwt_issuers: Vec<ed25519::PublicKey>,
}
impl UpgradeModeAttestation {
@@ -57,26 +45,17 @@ impl UpgradeModeAttestationContent {
}
}
pub fn generate_new_attestation(
key: &ed25519::PrivateKey,
authorised_jwt_issuers: Vec<ed25519::PublicKey>,
) -> UpgradeModeAttestation {
generate_new_attestation_with_starting_time(
key,
authorised_jwt_issuers,
OffsetDateTime::now_utc(),
)
pub fn generate_new_attestation(key: &ed25519::PrivateKey) -> UpgradeModeAttestation {
generate_new_attestation_with_starting_time(key, OffsetDateTime::now_utc())
}
pub fn generate_new_attestation_with_starting_time(
key: &ed25519::PrivateKey,
authorised_jwt_issuers: Vec<ed25519::PublicKey>,
starting_time: OffsetDateTime,
) -> UpgradeModeAttestation {
let content = UpgradeModeAttestationContent {
starting_time,
attester_public_key: key.into(),
authorised_jwt_issuers,
};
UpgradeModeAttestation {
signature: key.sign(content.as_json()),
@@ -84,19 +63,17 @@ pub fn generate_new_attestation_with_starting_time(
}
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn attempt_retrieve_attestation(
pub async fn attempt_retrieve(
url: &str,
user_agent: Option<nym_http_api_client::UserAgent>,
) -> Result<Option<UpgradeModeAttestation>, crate::UpgradeModeCheckError> {
let retrieval_failure = |source| crate::UpgradeModeCheckError::AttestationRetrievalFailure {
) -> Result<Option<UpgradeModeAttestation>, UpgradeModeCheckError> {
let retrieval_failure = |source| UpgradeModeCheckError::AttestationRetrievalFailure {
url: url.to_string(),
source,
};
let attestation = reqwest::ClientBuilder::new()
.user_agent(user_agent.unwrap_or_else(|| nym_http_api_client::generate_user_agent!()))
.timeout(std::time::Duration::from_secs(5))
.user_agent(generate_user_agent!())
.timeout(Duration::from_secs(5))
.build()
.map_err(retrieval_failure)?
.get(url)
@@ -124,20 +101,13 @@ mod tests {
163, 122, 170, 79, 198, 87, 85, 36, 29, 243, 92, 64, 161,
])?;
let authorised_jwt_issuers = vec![ed25519::PublicKey::from_base58_string(
"Be9wH7xuXBRJAuV1pC7MALZv6a61RvWQ3SypsNarqTt",
)?];
let attestation = generate_new_attestation_with_starting_time(
&key,
authorised_jwt_issuers,
starting_time,
);
let attestation = generate_new_attestation_with_starting_time(&key, starting_time);
let attestation_json = serde_json::to_string(&attestation)?;
let attestation_content_json = attestation.content.as_json();
let expected_attestation = r#"{"type":"upgrade_mode","starting_time":"2021-08-23T12:00:00Z","attester_public_key":"3pkFcBXCEmbmXBT2G8CkFMuKisJcH54mbBGvncHaDibt","authorised_jwt_issuers":["Be9wH7xuXBRJAuV1pC7MALZv6a61RvWQ3SypsNarqTt"],"signature":"5Kt9dfwvnkdnDcENbwNyitrxghyckWUYycBv8jUUn7hJUMohWEMc6otb3scXQfCrAGSE7FD5m7kr6auBmkAmfczY"}"#;
let expected_content = r#"{"type":"upgrade_mode","starting_time":"2021-08-23T12:00:00Z","attester_public_key":"3pkFcBXCEmbmXBT2G8CkFMuKisJcH54mbBGvncHaDibt","authorised_jwt_issuers":["Be9wH7xuXBRJAuV1pC7MALZv6a61RvWQ3SypsNarqTt"]}"#;
let expected_attestation = r#"{"type":"upgrade_mode","starting_time":1629720000,"attester_public_key":"3pkFcBXCEmbmXBT2G8CkFMuKisJcH54mbBGvncHaDibt","signature":"5rWUr2ypaDTtrMKegMP3tQkkZGFAuhNTnEVCVe5Azv6QqvLzoGdQiMkFmeyhDd1XSfoXpL9fFM58rsdA1kf4GYMM"}"#;
let expected_content = r#"{"type":"upgrade_mode","starting_time":1629720000,"attester_public_key":"3pkFcBXCEmbmXBT2G8CkFMuKisJcH54mbBGvncHaDibt"}"#;
assert_eq!(attestation_content_json, expected_content);
assert_eq!(attestation_json, expected_attestation);
-3
View File
@@ -12,9 +12,6 @@ pub enum UpgradeModeCheckError {
#[error("the jwt metadata didn't contain explicit public key")]
MissingTokenPublicKey,
#[error("the jwt signer does not appear in the authorised attestation set")]
UnauthorisedIssuer,
#[error("the attached public key was not valid ed25519 public key")]
MalformedEd25519PublicKey { source: Ed25519RecoveryError },
+5 -27
View File
@@ -65,12 +65,6 @@ pub fn validate_upgrade_mode_jwt(
.map_err(|source| UpgradeModeCheckError::JwtVerificationFailure { source })?
.custom;
// jwt itself is cryptographically valid,
// but let's see if this entity has been permitted to issue the token in the first place
if !attestation.authorised_to_issue_jwt(&ed25519_pub_key) {
return Err(UpgradeModeCheckError::UnauthorisedIssuer);
}
Ok(attestation)
}
@@ -79,7 +73,6 @@ mod tests {
use super::*;
use crate::generate_new_attestation;
use nym_crypto::asymmetric::ed25519;
use nym_test_utils::helpers::deterministic_rng;
#[test]
fn generate_and_validate_jwt() {
@@ -93,25 +86,15 @@ mod tests {
2, 52, 215, 241, 219, 200, 18, 159, 241, 76, 111, 42, 32,
])
.unwrap();
let jwt_keys = ed25519::KeyPair::from(jwt_key);
let keys = ed25519::KeyPair::from(jwt_key);
let mut rng = deterministic_rng();
let unauthorised_jwt_keys = ed25519::KeyPair::new(&mut rng);
let attestation = generate_new_attestation(&attestation_key, vec![*jwt_keys.public_key()]);
let attestation = generate_new_attestation(&attestation_key);
let jwt_issuer = generate_jwt_for_upgrade_mode_attestation(
attestation.clone(),
attestation,
Duration::from_secs(60 * 60),
&jwt_keys,
&keys,
Some("nym-credential-proxy"),
);
let unauthorised_jwt = generate_jwt_for_upgrade_mode_attestation(
attestation.clone(),
Duration::from_secs(60 * 60),
&unauthorised_jwt_keys,
Some("nym-credential-proxy"),
);
// we expect 'nym-credential-proxy' issuer
assert!(validate_upgrade_mode_jwt(&jwt_issuer, Some("nym-credential-proxy")).is_ok());
@@ -121,15 +104,10 @@ mod tests {
// we expect another-issuer
assert!(validate_upgrade_mode_jwt(&jwt_issuer, Some("another-issuer")).is_err());
// the key is not in the authorised set inside the attestation
assert!(
validate_upgrade_mode_jwt(&unauthorised_jwt, Some("nym-credential-proxy")).is_err()
);
let jwt_no_issuer = generate_jwt_for_upgrade_mode_attestation(
attestation,
Duration::from_secs(60 * 60),
&jwt_keys,
&keys,
None,
);
// we expect 'nym-credential-proxy' issuer
+2 -4
View File
@@ -6,10 +6,8 @@ pub(crate) mod error;
pub(crate) mod jwt;
pub use attestation::{
UpgradeModeAttestation, generate_new_attestation, generate_new_attestation_with_starting_time,
UpgradeModeAttestation, attempt_retrieve, generate_new_attestation,
generate_new_attestation_with_starting_time,
};
pub use error::UpgradeModeCheckError;
pub use jwt::{generate_jwt_for_upgrade_mode_attestation, validate_upgrade_mode_jwt};
#[cfg(not(target_arch = "wasm32"))]
pub use attestation::attempt_retrieve_attestation;
+1 -1
View File
@@ -259,7 +259,7 @@ pub fn migrate(deps: DepsMut<'_>, env: Env, _msg: MigrateMsg) -> Result<Response
set_build_information!(deps.storage)?;
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
crate::queued_migrations::introduce_historical_epochs(deps, env)?;
// crate::queued_migrations::introduce_historical_epochs(deps, env)?;
Ok(Response::new())
}
@@ -1,26 +1,27 @@
```tsx copy filename="mixFetchExample.tsx"
import React, { useState } from "react";
import CircularProgress from "@mui/material/CircularProgress";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import { mixFetch } from "@nymproject/mix-fetch-full-fat";
import Stack from "@mui/material/Stack";
import Paper from "@mui/material/Paper";
import type { SetupMixFetchOps } from "@nymproject/mix-fetch-full-fat";
```ts copy filename="mixFetchExample.tsx"
import React, { useState } from 'react';
import CircularProgress from '@mui/material/CircularProgress';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import { mixFetch } from '@nymproject/mix-fetch-full-fat';
import Stack from '@mui/material/Stack';
import Paper from '@mui/material/Paper';
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
const defaultUrl = "https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
const args = { mode: "unsafe-ignore-cors" };
const defaultUrl = 'https://nym.com/favicon.svg';
const args = { mode: 'unsafe-ignore-cors' };
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: "2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW", // with WSS
// preferredNetworkRequester:
// "CTDxrcXgrZHWyCWnuCgjpJPghQUcEVz1HkhUr5mGdFnT.3UAww1YWNyVNYNWFQL1LaHYouQtDiXBGK5GiDZgpXkTK@2RFtU5BwxvJJXagAWAEuaPgb5ZVPRoy2542TT93Edw6v",
preferredGateway: '6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B', // with WSS
preferredNetworkRequester:
'8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
forceTls: true, // force WSS
extra: {},
};
export const MixFetch = () => {
@@ -44,7 +45,7 @@ export const MixFetch = () => {
};
return (
<div style={{ marginTop: "1rem" }}>
<div style={{ marginTop: '1rem' }}>
<Stack direction="row">
<TextField
disabled={busy}
@@ -55,12 +56,7 @@ export const MixFetch = () => {
defaultValue={defaultUrl}
onChange={(e) => setUrl(e.target.value)}
/>
<Button
variant="outlined"
disabled={busy}
sx={{ marginLeft: "1rem" }}
onClick={handleFetch}
>
<Button variant="outlined" disabled={busy} sx={{ marginLeft: '1rem' }} onClick={handleFetch}>
Fetch
</Button>
</Stack>
@@ -1,19 +1,15 @@
```ts copy filename="MixnetWASMClientExample.tsx"
import React, { useEffect, useState } from "react";
import {
createNymMixnetClient,
NymMixnetClient,
Payload,
} from "@nymproject/sdk-full-fat";
import Box from "@mui/material/Box";
import CircularProgress from "@mui/material/CircularProgress";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import React, { useEffect, useState } from 'react';
import { createNymMixnetClient, NymMixnetClient, Payload } from '@nymproject/sdk-full-fat';
import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import Stack from '@mui/material/Stack';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
const nymApiUrl = "https://validator.nymtech.net/api";
const nymApiUrl = 'https://validator.nymtech.net/api';
export const Traffic = () => {
const [nym, setNym] = useState<NymMixnetClient>();
@@ -21,31 +17,27 @@ export const Traffic = () => {
const [recipient, setRecipient] = useState<string>();
const [payload, setPayload] = useState<Payload>();
const [receivedMessage, setReceivedMessage] = useState<string>();
const [buttonEnabled, setButtonEnabled] = useState<boolean>(false);
const init = async () => {
const client = await createNymMixnetClient();
setNym(client);
// start the client and connect to a gateway
await client?.client.start({
clientId: crypto.randomUUID(),
nymApiUrl,
forceTls: true, // force WSS
});
// check when is connected and set the self address
client?.events.subscribeToConnected((e) => {
const { address } = e.args;
setSelfAddress(address);
});
// show whether the client is ready or not
client?.events.subscribeToLoaded((e) => {
console.log("Client ready: ", e.args);
console.log('Client ready: ', e.args);
});
// show message payload content when received
client?.events.subscribeToTextMessageReceivedEvent((e) => {
console.log(e.args.payload);
setReceivedMessage(e.args.payload);
@@ -56,8 +48,7 @@ export const Traffic = () => {
await nym?.client.stop();
};
const send = () =>
payload && recipient && nym?.client.send({ payload, recipient });
const send = () => nym.client.send({ payload, recipient });
useEffect(() => {
init();
@@ -66,17 +57,9 @@ export const Traffic = () => {
};
}, []);
useEffect(() => {
if (recipient && payload) {
setButtonEnabled(true);
} else {
setButtonEnabled(false);
}
}, [recipient, payload]);
if (!nym || !selfAddress) {
return (
<Box sx={{ display: "flex" }}>
<Box sx={{ display: 'flex' }}>
<CircularProgress />
</Box>
);
@@ -84,10 +67,10 @@ export const Traffic = () => {
return (
<Box padding={3}>
<Paper style={{ marginTop: "1rem", padding: "2rem" }}>
<Paper style={{ marginTop: '1rem', padding: '2rem' }}>
<Stack spacing={3}>
<Typography variant="body1">My self address is:</Typography>
<Typography variant="body1">{selfAddress || "loading"}</Typography>
<Typography variant="body1">{selfAddress || 'loading'}</Typography>
<Typography variant="h5">Communication through the Mixnet</Typography>
<TextField
type="text"
@@ -100,22 +83,15 @@ export const Traffic = () => {
placeholder="Message to send"
multiline
rows={4}
onChange={(e) =>
setPayload({ message: e.target.value, mimeType: "text/plain" })
}
onChange={(e) => setPayload({ message: e.target.value, mimeType: 'text/plain' })}
size="small"
/>
<Button
variant="outlined"
onClick={() => send()}
disabled={!buttonEnabled}
sx={{ width: "fit-content" }}
>
<Button variant="outlined" onClick={() => send()} disabled={!payload || !recipient} sx={{width: 'fit-content'}}>
Send
</Button>
</Stack>
{receivedMessage && (
<Stack spacing={3} style={{ marginTop: "1rem" }}>
<Stack spacing={3} style={{ marginTop: '1rem' }}>
<Typography variant="h5">Message Received!</Typography>
<Typography fontFamily="monospace">{receivedMessage}</Typography>
</Stack>
+4 -4
View File
@@ -9,13 +9,13 @@ import Stack from "@mui/material/Stack";
import Paper from "@mui/material/Paper";
import type { SetupMixFetchOps } from "@nymproject/mix-fetch-full-fat";
const defaultUrl = "https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
const defaultUrl = "https://nym.com/favicon.svg";
const args = { mode: "unsafe-ignore-cors" };
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: "2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW", // with WSS
// preferredNetworkRequester:
// "CTDxrcXgrZHWyCWnuCgjpJPghQUcEVz1HkhUr5mGdFnT.3UAww1YWNyVNYNWFQL1LaHYouQtDiXBGK5GiDZgpXkTK@2RFtU5BwxvJJXagAWAEuaPgb5ZVPRoy2542TT93Edw6v",
preferredGateway: "6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B", // with WSS
preferredNetworkRequester:
"8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B",
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
@@ -1,2 +0,0 @@
export const ICMP =
"ICMP (Internet Control Message Protocol) is a network layer protocol used for sending error messages and operational information regarding network conditions. It is commonly utilized for diagnostic purposes, such as sending ping requests to check the reachability of a host. ICMP helps in managing and controlling network traffic by providing feedback about issues in the communication environment.";
@@ -1,3 +0,0 @@
export const IPR =
"IPR (IP Packet Router) is a component that routes packets to the Internet on behalf of clients within the Nym network. It uses multiplexed streams, NAT, and SURBs for anonymity.";
@@ -1,93 +0,0 @@
import { Callout } from 'nextra/components';
## Mixnet: Node Performance Calculation
Performance is a value between `0` and `1`. The final performance number is a result of multiplying [config score](#config-score-calculation) and [routing score](#routing-score-calculation).
<Callout type="info" emoji="📌">
> **node_performance = config_score \* routing_score**
</Callout>
Performance value is an average of last 24h.
<Callout>
All parameters regarding performance score can be browsed or pull live from:
`https://validator.nymtech.net/api/v1/nym-nodes/annotation/<NODE_ID>`
In case you don't know your nodes `NODE_ID`, it's easy to find as long as your node is bonded. Visit [validator.nymtech.net/api/v1/nym-nodes/bonded](https://validator.nymtech.net/api/v1/nym-nodes/bonded) and search your node using `identity_key` or bonding Nyx account address (denoted as `owner`).
</Callout>
### Config Score Calculation
Config score is in place to ensure that the node configuration is done properly so the node is eligible for taking part in Nym network. The API looks into these paramteres:
1. If the node binary is `nym-node` (not legacy `nym-mixnode` or `nym-gateway`): `1` if `True`, `0` if `False`
2. If [Terms & Conditions](/operators/nodes/nym-node/setup.mdx#terms--conditions) are accepted: `1` if `True`, `0` if `False`
3. If the nodes self described endpoint is available: `1` if `True`, `0` if `False`
4. Version of `nym-node` binary: decreasing weight for outdated versions, as [explained below](#versions-behind-calculation)
**The `config_score` calculation formula:**
<Callout type="info" emoji="📌">
> **config_score = is_tc_accepted \* is_nym-node_binary \* self_described_api_available \* ( 0.995 ^ ( ( X * versions_behind) ^ 1.65 ) )**
</Callout>
First three points have binary values of either `0` or `1`, with a following logic:
| **Run `nym-node` binary** | **T&C's accepted** | **Self described available** | **Value** |
| :-- | :-- | :-- | ---: |
| **True** | **True** | **True** | **1** |
| True | False | False | 0 |
| True | True | False | 0 |
| False | True | True | 0 |
| False | False | True | 0 |
| True | False | True | 0 |
| False | False | False | 0 |
| False | True | False | 0 |
**Only if ALL conditions above are `True` the node can have any chance to be selected, as otherwise the probability will always be 0.**
<Callout type="info" emoji="️">
Besides these values, the API also checks whether the node is bonded in Mixnet smart contract as a Nym Node or legacy node (Mixnode or Gateway). **Only nodes bonded as Nym Node in Mixnet smart contract can be selected to the Rewrded set. Thus, if you haven't migrated your node yet, please [follow these steps](/operators/nodes/nym-node/bonding#migrate-to-nym-node-in-mixnet-smart-contract)!**
</ Callout>
#### Versions Behind Calculation
From release `2024.14-crunch` (`nym-node v1.2.0`), the `config_score` parameter takes into account also nodes version (denoted as `versions_behind`). The "current version" is the one marked as `Latest` in the [repository](https://github.com/nymtech/nym/releases/). The parameter `versions_behind` indicates the number of versions between the `Latest` version and the version run by the node, and it is factored into the config score with this formula:
<Callout type="info" emoji="📌">
> **0.995 ^ ( ( X * versions_behind ) ^ 1.65 )**
>
> where: <br />
> **X = 1; for patches** <br />
> **X = 10; for minor versions** <br />
> **X = 100; for major versions**
</Callout>
> The exact parameters are live accessible on [`/v1/status/config-score-details`](https://validator.nymtech.net/api/swagger/index.html#/Status/config_score_details).
Our versioning convention is: `major_version . minor_version . patch`
For example `nym-node` on version `1.2.0` is on 1st major version, 2nd minor and 0 patches.
Note that the `X` multiplier heavily lowers the `config_score` when nodes are outdated with respect to more significant updates. See the the table and graph below:
| **Version behind** | **Patches (X = 1)** | **Minor versions (X = 10)** | **Major versions (X = 100)** |
| :-- | --: | --: | --: |
| 0 (current version) | 1.0 | 1.0 | 1.0 |
| 1 | 0.995 | 0.7994 | 0.0000 |
| 2 | 0.9844 | 0.4953 | 0.0000 |
| 3 | 0.9698 | 0.2536 | 0.0000 |
| 4 | 0.9518 | 0.1102 | 0.0000 |
| 5 | 0.9311 | 0.0413 | 0.0000 |
![](/images/operators/tokenomics/reward_version_graph.png)
As you can see above, the algorithm is designed to give maximum selection score (`1`) to the latest version, while non-upgraded nodes receive a lower score. The score decreases faster when the node has failed to make a major version upgrade, and slower when the node is behind only with minor updates. This scoring de-prioritizes the selection of outdated nodes, even if their saturation and performance are high. Nodes are selected probabilistically in each epoch (60 min), according to their scores, to be part of the Rewarded set. This scoring mechanism gives priority to the operators running up-to-date nodes, ensuring that the network is as updated as possible.
### Routing Score Calculation
Routing score is measured by Nym Network Monitor which sends thousands of packages through different routes every 15 minutes and measures how many were dropped on the way. Test result represents percentage of packets succesfully returned which are then converted into floats bettween `0` and `1`.
@@ -1,223 +0,0 @@
import { Callout } from 'nextra/components';
import { Steps } from 'nextra/components';
import { Green, Yellow, Red, Gray } from 'components/severity.jsx'
import { IPR } from 'components/operators/snippets/ipr.js';
import { ICMP } from 'components/operators/snippets/icmp.js';
## WireGuard: Gateways Performance Calculation
> If you are looking for a page with a guide to run your own `gateway-probe` instance, go [here](/operators/performance-and-testing/gateway-probe).
<Callout type="info" emoji="️">
**Note that there is only one binary `nym-node` for [all network functionalities](/operators/nodes/nym-node/setup#functionality-mode) (defined by a positional argument `--mode`).**
When we say *Exit Gateway* in connection to Wireguard, we talk about a `nym-node` running in a mode Exit Gateway (`--mode exit-gateway`) with Wireguard enabled. Such node is listed in [NymVPN application](https://nym.com) as an Exit Gateway and an Entry Gateway for both dVPN (2-hop) and Mixnet (5-hop) options. That's because every Exit Gateway can serve as an Entry, not vice versa for Mixnet and every Gateway with Wireguard enabled option (`--wireguard-enabled true`) can always serve as both Entry and Exit for dVPN (provided that they are correctly [configured](/operators/nodes/nym-node/configuration)).
</Callout>
**Please note that even the most perfectly configured Gateway, running on the strongest machine may not always be listed as a suggested node in the app. If you are interested to learn the details why, check out the [Gateway Probe Details page](/operators/performance-and-testing/gateway-probe-details).**
### Summary
A simplified explanation of node performance measuring is that the [Node Status API](https://node-status.nym.com) runs [gateway probes](/operators/performance-and-testing/gateway-probe) that connect to gateways to:
- Check the configuration of gateways
- Checks a list of capabilities (e.g. can route IPv4 traffic in mixnet mode)
- Checks a list of configuration (e.g. runs <abbr title={IPR}>IPR</abbr>, has exit policy)
- Acts like a user:
- Registers a mixnet client
- Registers a wireguard peer and tops up bandwidth with a zk-nym
- Sends <abbr title={ICMP}>ICMP</abbr> ping packets
- Downloads files
The results are collected and stored in the [Node Status API](https://node-status.nym.com) and can be also veiwed per node in [Node Status Observatory](https://harbourmaster.nymtech.net).
The [NymVPN API directory](https://nymvpn.com/api/public/v1/directory/gateways) cache uses the output of the gateway probes to calculate and display hints to users about the contention on each gateway and what they might expect if they use the gateway. The section [*Gateway Roles & Requirements* below](#gateway-roles--requirements) explains how are these stats measured and what is their significance.
### Performance Score
Gateways are listed in NymVPN application displaying various stats.
![](/images/operators/nym-vpn-sceenshots/nym-vpn-score1.png)
There are four colored score bars.
![](/images/operators/nym-vpn-sceenshots/nym-vpn-score2.png)
This is how they are defined:
<Steps>
###### 1. Overall performance:
Calculated by mixnet performance score multiplied by WireGuard performance, this is the formula:
<Callout type="info" emoji="📌">
> **mixnet_performance * (download_speed_score * ping_ips_performance_v4)**
</Callout>
- <Green>High</Green>: `> 75%`
- <Yellow>Medium</Yellow>: `> 50%`
- <Red>Low</Red>: > `10%`
- <Gray>Offline</Gray>: `≤ 10%`
- **Download speed scoring:**
<Callout type="info" emoji="📌">
> **\> 5 Mbps = 1.0** <br />
> **\> 2 Mbps = 0.75** <br />
> **\> 1 Mbps = 0.5** <br />
> **\> 0.5 Mbps = 0.25** <br />
> **otherwise = 0.1**
</Callout>
###### 2. Server Load:
A load indicator based on ping success
- <Green>Low</Green>: `> 80%` ping success (low load)
- <Yellow>Medium</Yellow>: `> 40%` ping success (medium load)
- <Red>High</Red>: `≤ 40%` ping success (node under stress, high load)
###### 3. Uptime:
A routing score of Mixnet (5-hop) is indicated this way:
- <Green>High</Green>: `> 80%` mixnet packet delivery success
- <Yellow>Medium</Yellow>: `> 60%` mixnet packet delivery success
- <Red>Low</Red>: `> 10%` mixnet packet delivery success
- <Gray>Offline</Gray>: `≤ 10%` mixnet packet delivery success
</Steps>
Note that there are caches in place to keep various dept of details to provide info for different pieces of the software. Their timing differs and it can lead to small discrepancies of outcome in between multiple clients observed by users at the same time.
See the [*Gateway Roles & Requirements*](#gateway-roles--requirements) to understand how these stats get measured.
<Callout>
You can find more details [directly in the code](https://github.com/nymtech/nym-vpn-client/blob/develop/nym-vpn-app/src/hooks/useScore.ts#L10).
</Callout>
### Gateway Roles & Requirements
A node configuration break down coming alongside basic [server configuration](/operators/nodes/preliminary-steps/vps-setup) required for the nodes in specific roles:
- **Entry Gateway - Mixnet:**
- Node configured (running with `--mode` argument) as `entry-gateway` or `exit-gateway` (as Exit works as Entry too)
- `WS/WSS` ports (9000/9001) accessible
- [IPv6 configured](/operators/nodes/nym-node/configuration#quick-ipv6-check)
- **Exit Gateway - Mixnet:**
- Node configured (running with `--mode` argument) as `exit-gateway`, that configuration ensures that:
- <abbr title={IPR}>IPR</abbr> address present
- NR address present
- [IPv6 configured](/operators/nodes/nym-node/configuration#quick-ipv6-check)
- NymTun configured - using [NetworkTunnelManager](/operators/nodes/nym-node/configuration#routing-configuration)
- **Entry & Exit Gateway - dVPN (WireGuard mode):**
- Node configured as Wireguard node: `--wireguard-enabled true`
- Authenticator address present
- NymWG configured - using [NetworkTunnelManager](/operators/nodes/nym-node/configuration#routing-configuration)
- [Metadata port open for WG](/operators/nodes/nym-node/configuration#fixing-metadata-port-showing-not-open-in-probe-results)
- Wireguard exit policy [configured](/operators/nodes/nym-node/configuration#wireguard-exit-policy-configuration)
- [IPv6 configured](/operators/nodes/nym-node/configuration#quick-ipv6-check)
- Recommended: [QUIC bridge setup](/operators/nodes/nym-node/configuration#quic-transport-bridge-deployment)
### How Probe Works
Nym Gateway probe is perfomance measurement program running tests through various routes. Operators can run their [local instance of Gateway probe](/operators/performance-and-testing/gateway-probe) or use visit of our dashboards to conveniently see probe results.
- [Node Status dashboard](https://node-status.nym.com/dvpn): Shows latest probe results on one board
- [Nym Node Status Observatory](https://harbourmaster.nymtech.net): New version of a good old Nym Harbourmaster, allowing operators preview stats of each node
The following sections explain what is measured in order to collect stats to the [API enpoint](https://nymvpn.com/api/public/v1/directory/gateways) and in the section [*Complete Setup Checklist*](#complete-setup-checklist) below you can see the order of measurements.
#### Entry Gateway - Mixnet (5-hop)
**Tests:**
- `can_connect`: Can connect to client WS/WSS endpoint (ports 9000/9001)
- `can_route`: Entry gateway correctly forwards packets into the mixnet
**To Pass:**
- Expose correct `WS` and or `WSS` ports (`9000`/ `9001`)
- Ensure DNS A/AAAA records resolve correctly
- Verify IPv6 is actually reachable (not just in DNS)
- Keep node online and monitor packet loss
#### Exit Gateway (IPR) - Mixnet (5-hop)
**Tests:**
- `can_connect`: Can connect to <abbr title={IPR}>IPR</abbr>
- `can_route_ip_v4`: IPv4 routing works
- `can_route_ip_external_v4`: IPv4 routing to external internet works
- `can_route_ip_v6`: IPv6 routing works
- `can_route_ip_external_v6`: IPv6 routing to external internet works
**To Pass:**
- Ensure upstream routing and NAT allow both IPv4 and IPv6 traffic to external internet
- Check system firewall rules (bidirectional)
- Confirm outbound reachability to common external hosts on both stacks
- Run node with `mode=exit-gateway` - this by default includes entry-gateway mode.
#### Gateways - WireGuard (dVPN / 2-hop)
**Tests:**
- `can_register`: Authentication flow completes successfully
- `can_handshake`: WireGuard handshake succeeds
- `can_resolve_dns`: DNS resolution works
- `can_query_metadata_v4`: Can query metadata endpoint
- `ping_hosts_performance` / `ping_ips_performance`: Latency and packet loss metrics
**To Pass:**
- Open WireGuard UDP port `51822` (public)
- Ensure metadata TCP port `51830` is accessible inside the WireGuard tunnel (at `10.1.0.1:51830`) for bandwidth queries/topup
- Keep QUIC bridge (`UDP 4443`) healthy if deployed
- Reduce server latency/jitter (depends on provider and location)
- Ensure DNS resolvers are reliable
- Pass the `--wireguard-enabled true` flag in the node config
### Probe Freshness
- Probes run continuously; each node is typically tested every ~2 hours
- If a probe fails or times out, the gateway will have stale probe data until the next successful probe completes
- `last_updated_utc`: Timestamp indicating when the last successful probe test completed
See [*Complete Setup Checklist*](#complete-setup-checklist) to understand the order of tests.
### Complete Setup Checklist
**If one test fails, the following points don't get checked!**
#### On-Chain & Configuration
- [ ] [Bonded on-chain](/operators/nodes/nym-node/bonding) with [correct role](/operators/nodes/nym-node/setup#functionality-mode) (`exit-gateway`) and correct ports opened exposed
- [ ] Self-description endpoint is reachable
- [ ] [T&Cs accepted](/operators/nodes/nym-node/setup#terms--conditions)
#### Network & Ports
- [ ] **Mixnet**: Port `1789` (mixnet) accessible; ports `9000` (`WS`) and `9001` (`WSS`) accessible with valid [TLS for WSS](/operators/nodes/nym-node/configuration/proxy-configuration#reverse-proxy-configuration)
- [ ] [**QUIC bridge**](/operators/nodes/nym-node/configuration#quic-transport-bridge-deployment) (if used): UDP 4443 with valid addresses and SNI host
- [ ] [**WireGuard**](/operators/nodes/nym-node/configuration#routing-configuration): `UDP 51822` open (public); `TCP 51830` accessible [inside WireGuard tunnel](/operators/nodes/nym-node/configuration#fixing-metadata-port-showing-not-open-in-probe-results) (`10.1.0.1:51830`); `--wireguard-enabled true` set in node config
- [ ] **Exit (<abbr title={IPR}>IPR</abbr>)**: [IPv4 and IPv6](/operators/nodes/nym-node/configuration#routing-configuration) routing to external internet configured
- [ ] **DNS**: A/AAAA records correct; IPv6 actually reachable (test, don't just add DNS)
#### Performance Targets
- [ ] Achieve `≥ 60%` mixnet performance (Medium)
- [ ] Aim for `≥ 80%` (High) for optimal visibility
- [ ] Keep latency and packet loss low (excessive load reduces performance scores)
- [ ] Keep uptime high (network monitor tracks this)
#### Probe Results (Verify All Pass)
- [ ] Entry: `can_connect=true` and `can_route=true`
- [ ] Exit: `can_connect=true` and all v4/v6 routing flags are `true`
- [ ] WireGuard: `can_register`, `can_handshake`, `can_resolve_dns`, and `can_query_metadata_v4` all pass; ping metrics good
#### Recommended
- [ ] [WSS enabled](/operators/nodes/nym-node/configuration/proxy-configuration) (connections may fail on clients if only WS is available)
- [ ] [QUIC bridge](/operators/nodes/nym-node/configuration#quic-transport-bridge-deployment) operational (ensures your node appears when users enable QUIC only filter on the app)
- [ ] Accurate geo/IP information (country/region/residential filters rely on this)
- [ ] Stay on most recent supported binary version - [old versions are penalised](/operators/performance-and-testing#versions-behind-calculation)
@@ -3,7 +3,7 @@ import { Callout } from 'nextra/components';
import { AccordionTemplate } from 'components/accordion-template.tsx';
<Callout type="info" emoji="️">
**QUIC bridge is a requirement for all nodes which enable Wireguard functionality. Note that it this feature is compatible with nodes from `v1.19.0` (platform release [`v2025.18-jarlsberg`](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.17-isabirra)) and newer!**
**QUIC bridge is a requirement for all nodes which enable Wireguard functionality. Note that it this feature is compatible with nodes from `v1.18.0` (platform release [`v2025.17-isabirra`](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.17-isabirra)) and newer!**
</ Callout>
Nym Network uses various [transport bridges](https://github.com/nymtech/nym-bridges/blob/main/README.md) for routing the packets. Right now operators need to configure [our implementation](https://github.com/nymtech/nym-bridges/tree/main/nym-bridge) of general-purpose transport layer network protocol called [QUIC](https://en.wikipedia.org/wiki/QUIC).
@@ -12,43 +12,36 @@ Operators can use [Nym Bridge Configuration Tool](https://github.com/nymtech/nym
**We recommend a more convenient QUIC bridge deployment using a script [`quic_bridge_deployment.sh`](https://github.com/nymtech/nym/blob/develop/scripts/nym-node-setup/quic_bridge_deployment.sh), following the steps below.**
<Callout type="warning" emoji="⚠️">
This script assumes that you follow the convention of running a `nym-node` as `root`. In case you run as a [non-root user](/operators/nodes/nym-node/configuration#running-nym-node-as-a-non-root), please follow [manual QUIC bridge setup](https://github.com/nymtech/nym-bridges/blob/main/README.md) and ask for support in the [Matrix channel](https://matrix.to/#/#operators:nymtech.chat).
</Callout>
<Steps>
###### 1. Download [`quic_bridge_deployment.sh`](https://github.com/nymtech/nym/blob/develop/scripts/nym-node-setup/quic_bridge_deployment.sh) script
- SSH to your server
- **Run as root**
- Download the script and make executable
```sh
curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/quic_bridge_deployment.sh -o quic_bridge_deployment.sh && \
wget https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/quic_bridge_deployment.sh && \
chmod +x quic_bridge_deployment.sh
```
###### 2. Run the script with `full_bridge_setup` command
- Optional: open `tmux` in case you will need to run another commands on the VPS
- Run the script with a command `full_bridge_setup`
```sh
./quic_bridge_deployment.sh full_bridge_setup
./nym-node-setup/quic_bridge_deployment.sh full_bridge_setup
```
###### 3. Follow the interactive prompts
- Make sure you don't just press enter to insert default values if your setup is different, for example in case of path to the config file
- When you are asked for bridge binary URL, look here for one to match your system: [builds.ci.nymte.ch/QUIC](https://builds.ci.nymte.ch/QUIC/)
- When you are asked to enter forward address, it's your IPv4 used for bonding the node, alongside port `:51822` (an example: `172.232.238.161:51822`)
- To find out your IP address you can always run:
- IPv4: `curl -4 https://ifconfig.co/ip`
- IPv6: `curl -6 https://ifconfig.co/ip`
- **For all prompts with default options, we highly recommend to stick to default (press enter)**
###### 4. Validate
- After running the script, ensure that the bridges are running correctly:
```sh
systemctl status nym-bridge.service
```
###### 5. Restart `nym-node` service
###### 4. Restart the node service
- When done with the deployment, please restart your node systemd service
```sh
service nym-node restart && journalctl -u nym-node.service -f --all
```
</Steps>
Congratulation, you deployed QUIC transport bridge! The script offers a standalone tweaks and checks, you can always run it without any argument to see all the options:
@@ -59,27 +52,51 @@ Congratulation, you deployed QUIC transport bridge! The script offers a standalo
<AccordionTemplate name="Command output">
```shell
root@localhost:~# ./quic_bridge_deployment.sh
Logs are being saved locally to: /var/log/nym-bridge-helper.log
These logs never leave your machine.
iptables-persistent is already installed.
Usage: ./quic_bridge_deployment.sh [command] [options]
Nym QUIC Bridge Deployment Helper Script
Usage: ./quic_bridge_deployment.sh [command]
Bridge Installation & Configuration:
check_bridge_installation - Check bridge installation status
show_bridge_config - Display bridge configuration files
show_bridge_keys - Display bridge key information
show_bridge_info - Show comprehensive bridge information
verify_bridge_prerequisites - Verify all prerequisites are met
Commands:
full_bridge_setup - Run full setup
install_bridge_binary - Install nym-bridge (.deb; falls back to source build if libc too old)
install_bridge_cfg_tool - Install bridge-cfg (prebuilt; falls back to source build if libc too old)
run_bridge_cfg_generate - Generate bridges.toml
create_bridge_service - Setup systemd service (respects .deb-provided service)
adjust_ip_forwarding - Enable forwarding
apply_bridge_iptables_rules - NAT rules
configure_dns_and_icmp - Allow ICMP/DNS
Bridge Setup Commands:
install_bridge_binary - Download and install nym-bridge binary
generate_bridge_keys - Generate ED25519 bridge identity keys
create_client_params - Create client_bridge_params.json
create_bridge_config - Create bridges.toml configuration
create_bridge_service - Create systemd service file
full_bridge_setup - Interactive full bridge setup wizard
Network Configuration Commands:
adjust_ip_forwarding - Enable IPv4 and IPv6 forwarding
apply_bridge_iptables_rules - Apply iptables rules for QUIC bridge (nymwg)
configure_dns_and_icmp - Allow ICMP ping tests and configure DNS
remove_duplicate_bridge_rules - Remove duplicate iptables rules for nymwg
------------------------------------------
Script exited with errors (code: 1).
Check the log at: /var/log/nym-bridge-helper.log
------------------------------------------
Network Inspection Commands:
fetch_and_display_ipv6 - Show IPv6 on default network device
fetch_wg_ipv6_address - Fetch IPv6 for nymwg interface
check_bridge_iptables - Check iptables rules for nymwg
check_ipv6_ipv4_forwarding - Check IPv4 and IPv6 forwarding status
check_ip_routing - Display IP routing tables
Testing Commands:
perform_pings - Test IPv4 and IPv6 connectivity
test_bridge_connectivity - Comprehensive bridge connectivity test
Service Management Commands:
check_bridge_service_status - Check nym-bridge and nym-node service status
show_bridge_logs [lines] - Show recent nym-bridge logs (default: 50 lines)
Quick Start:
1. Run 'verify_bridge_prerequisites' to check prerequisites
2. Run 'check_bridge_installation' to verify installation
3. Run 'test_bridge_connectivity' to test connectivity
```
</AccordionTemplate>
@@ -87,9 +104,7 @@ Check the log at: /var/log/nym-bridge-helper.log
If you have followed the steps outlined above, but the metadata port is not shown as open in either the Node Status API's probe results or an explorer that gets its data from the API, see below:
<Steps>
**Run as root**
###### 1. Confirm correct `config.toml` private IPv4 address
###### 1.
Ensure that in your `config.toml` file, this value is set to the default one - any other value here will cause the metadata endpoint to fail:
```
@@ -100,7 +115,7 @@ private_ipv4 = '10.1.0.1'
Then restart your node.
###### 2. Open the needed port
###### 2.
Run this command if not already done:
```
ufw allow in on nymwg to any port 51830 proto tcp
@@ -116,7 +131,7 @@ Then ensure the metadata endpoint is listening from the correct address with:
netstat -an | egrep LISTEN | egrep "51830"
```
###### 3. Validate the setup
###### 3.
Once the Node Status API has run a probe on your node, the probe results will reflect this - `can_query_metadata_v4` will have `true` as a value.
The quickest way to check this is by using the [NymVPN API](https://nymvpn.com/api/public/v1/directory/gateways?show_vpn_only=true) and checking the same field:
@@ -1,266 +0,0 @@
import { Callout } from 'nextra/components';
import { Tabs } from 'nextra/components';
import { Steps } from 'nextra/components';
import { AccordionTemplate } from 'components/accordion-template.tsx';
export const ManagerIPOutput = () => (
<div>
Correct <code>./network-tunnel-manager.sh fetch_and_display_ipv6</code> output
</div>
);
export const ManagerTablesOutput = () => (
<div>
Correct <code>./network-tunnel-manager.sh check_nymtun_iptables</code> output
</div>
);
export const ShowTun = () => (
<div>
Correct <code>ip addr show nymtun0</code> output
</div>
);
<Callout>
We recommend operators to configure their `nym-node` with the full routing configuration.
However, most of the time the packets sent through the Mixnet are IPv4 based. The IPv6 packets are still pretty rare and therefore it's not mandatory from operational point of view to have this configuration implemented if you running only `mixnode` mode.
If you preparing to run a `nym-node` with all modes enabled in the future, this setup is required.
</Callout>
<Callout type="warning" emoji="⚠️">
Networking configuration across different ISPs and various operation systems does not have a generic solution. If the provided configuration setup doesn't solve your problem check out [IPv6 troubleshooting](/operators/troubleshooting/vps-isp.mdx#ipv6-troubleshooting) page. Be aware that you may have to do more research, customised adjustments or contact your ISP to change settings for your VPS.
</ Callout>
**Network Tunnel Manager ([`network-tunnel-manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh), NTM) is currently the one tool hadling the configuration of `nym-node` hosting server, according to the required design (node's [functionality](/operators/nodes/nym-node/setup#functionality-mode), WireGuard setup etc).**
**NTM cand administrate these areas:**
* IPv4 and IPv6 routing to the internet
* The `nymtun0` interface (Mixnet / 5-hop): dynamically managed by the `exit-gateway` service. When the service is stopped, `nymtun0` disappears, and when started, `nymtun0` is recreated.
* The `nymwg` interface (WG / 2-hop): used for creating a secure wireguard tunnel as part of the Nym Network configuration.
* `iptables` rules specific to `nymwg` to ensure proper routing and forwarding through the wireguard tunnel. The `nymwg` interface needs to be correctly configured and active for the related commands to function properly. This includes applying or removing iptables rules and running connectivity tests through the `nymwg` tunnel.
* WireGuard exit policy: Mixnet uses a common [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt), to apply the same for WG, the operators need to set that one up on their server using `iptables` rules.
* Testing and validating all above
**Before starting with the following configuration, make sure you have the [latest `nym-node` binary](https://github.com/nymtech/nym/releases) installed and your [VPS setup](../preliminary-steps/vps-setup.mdx) finished properly!**
<Callout type="warning" emoji="⚠️">
**Run the following steps as root!**
</ Callout>
**Choose configuration command according your setup**
<div>
<Tabs items={[
<strong>New <code>nym-node</ code> full configuration</strong>,
<strong>Existing <code>nym-node</ code> full configuration</strong>,
<strong>Step-by-step or Pprtial configuration</strong>
]} defaultIndex={0}>
<Tabs.Tab>
This design is meant for operators setting up a new node on a fresh machine and it will result with a complete server readiness for routing as Entry Gateway and Exit Gateway in both Mixnet and WireGuard mode.
<Steps>
###### 1. Download `network-tunnel-manager.sh`, make executable and run with `--help` command:
```sh
curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/network-tunnel-manager.sh -o network-tunnel-manager.sh && \
chmod +x network-tunnel-manager.sh && \
./network-tunnel-manager.sh --help
```
###### 2. Make sure your `nym-node` service is up and running and bonded
- **If you setting up a new node and not upgrading an existing one, keep it running and [bond](/operators/nodes/nym-node/bonding.mdx) your node now! Then come back here and follow the rest of the configuration.**
###### 3. Execute complete network configuration:
```sh
./network-tunnel-manager.sh complete_networking_setup
```
</ Steps>
</Tabs.Tab>
<Tabs.Tab>
This is meant for operators configuring an existing and bonded node and it will result with a complete server readiness for routing as Entry Gateway and Exit Gateway in both Mixnet and WireGuard mode.
<Steps>
###### 1. Download `network-tunnel-manager.sh`, make executable and run with `--help` command:
```sh
curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/network-tunnel-manager.sh -o network-tunnel-manager.sh && \
chmod +x network-tunnel-manager.sh && \
./network-tunnel-manager.sh --help
```
###### 2. Execute complete network configuration:
```sh
./network-tunnel-manager.sh complete_networking_setup
```
</ Steps>
</Tabs.Tab>
<Tabs.Tab>
<Steps>
This design is meant for operators who want to do their server configuration step by step or choose only some parts of the setup.
###### 1. Download `network-tunnel-manager.sh`, make executable and run with `--help` command:
```sh
curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/network-tunnel-manager.sh -o network-tunnel-manager.sh && \
chmod +x network-tunnel-manager.sh && \
./network-tunnel-manager.sh --help
```
###### 2. Make sure your `nym-node` service is up and running and bonded
- **If you setting up a new node and not upgrading an existing one, keep it running and [bond](/operators/nodes/nym-node/bonding.mdx) your node now! Then come back here and follow the rest of the configuration.**
###### 3. Choose steps according your need
> You should be certain in your selection when configuring only various parts of the server.
###### Setup IP tables rules
- Delete IP tables rules for IPv4 and IPv6 and apply new ones:
```sh
./network-tunnel-manager.sh remove_duplicate_rules nymtun0
./network-tunnel-manager.sh apply_iptables_rules
```
- The process may prompt you if you want to save current IPv4 and IPv6 rules, choose yes.
![](/images/operators/ip_table_prompt.png)
- At this point you should see a `global ipv6` address.
```sh
./network-tunnel-manager.sh fetch_and_display_ipv6
```
<br />
<AccordionTemplate name={<ManagerTablesOutput/>}>
```sh
iptables-persistent is already installed.
Using IPv6 address: 2001:db8:a160::1/112 #the address will be different for you
operation fetch_ipv6_address_nym_tun completed successfully.
```
</AccordionTemplate>
###### Check Nymtun IP tables:
```sh
./network-tunnel-manager.sh check_nymtun_iptables
```
- If there's no process running it wouldn't return anything.
- In case you see `nymtun0` but not active, this is probably because you are setting up a new (never bonded) node and not upgrading an existing one.
<br />
<AccordionTemplate name={<ManagerIPOutput/>}>
```sh
iptables-persistent is already installed.
network Device: eth0
---------------------------------------
inspecting IPv4 firewall rules...
Chain FORWARD (policy DROP 0 packets, 0 bytes)
0 0 ufw-reject-forward all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- nymtun0 eth0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- eth0 nymtun0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT all -- nymtun0 eth0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- eth0 nymtun0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT all -- nymtun0 eth0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- eth0 nymtun0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
---------------------------------------
inspecting IPv6 firewall rules...
Chain FORWARD (policy DROP 0 packets, 0 bytes)
0 0 ufw6-reject-forward all * * ::/0 ::/0
0 0 ACCEPT all eth0 nymtun0 ::/0 ::/0 state RELATED,ESTABLISHED
0 0 ACCEPT all nymtun0 eth0 ::/0 ::/0
0 0 ACCEPT all eth0 nymtun0 ::/0 ::/0 state RELATED,ESTABLISHED
0 0 ACCEPT all nymtun0 eth0 ::/0 ::/0
0 0 ACCEPT all eth0 nymtun0 ::/0 ::/0 state RELATED,ESTABLISHED
0 0 ACCEPT all nymtun0 eth0 ::/0 ::/0
operation check_nymtun_iptables completed successfully.
```
</AccordionTemplate>
###### Remove old and apply new rules for wireguad routing
```sh
../network-tunnel-manager.sh remove_duplicate_rules nymwg
./network-tunnel-manager.sh apply_iptables_rules_wg
```
###### Apply rules to configure DNS routing and allow ICMP piung test for node probing (network testing)
```sh
./network-tunnel-manager.sh configure_dns_and_icmp_wg
```
###### Adjust and validate IP forwarding
```sh
./network-tunnel-manager.sh adjust_ip_forwarding
./network-tunnel-manager.sh check_ipv6_ipv4_forwarding
```
###### Check `nymtun0` interface and test routing configuration
```sh
ip addr show nymtun0
```
<br />
<AccordionTemplate name={<ShowTun/>}>
```sh
# your addresses will be different
8: nymtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1420 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 10.0.0.1/16 scope global nymtun0
valid_lft forever preferred_lft forever
inet6 fc00::1/112 scope global
valid_lft forever preferred_lft forever
inet6 fe80::ad08:d167:5700:8c7c/64 scope link stable-privacy
valid_lft forever preferred_lft forever`
```
</AccordionTemplate>
- Validate your IPv6 and IPv4 networking by running a joke test via Mixnet:
```sh
./network-tunnel-manager.sh joke_through_the_mixnet
```
- Validate your tunneling by running a joke test via WG:
```sh
../network-tunnel-manager.sh joke_through_wg_tunnel
```
###### Enable wireguard
Now you can run your node with the `--wireguard-enabled true` flag or add it to your [systemd service config](#systemd). Restart your `nym-node` or [systemd](#2-following-steps-for-nym-nodes-running-as-systemd-service) service (recommended):
```sh
systemctl daemon-reload && service nym-node restart
```
- Optionally, you can check if the node is running correctly by monitoring the service logs:
```sh
journalctl -u nym-node.service -f -n 100
```
</ Steps>
</Tabs.Tab>
</Tabs>
</div>
<Callout type="info" emoji="️">
Note that the functionality the node runs in is decided by [arguments on the node itself / in node's `config.toml`](/operators/nodes/nym-node/setup#functionality-mode), this tool only prepares the server.
</ Callout>
Make sure that you get the validation of all connectivity. If there are still any problems, please refer to [troubleshooting section](/operators/troubleshooting/vps-isp.mdx#incorrect-gateway-network-check).
@@ -1,68 +0,0 @@
import { Callout } from 'nextra/components';
import { Tabs } from 'nextra/components';
import { Steps } from 'nextra/components';
import { AccordionTemplate } from 'components/accordion-template.tsx';
import ExitPolicyInstallOutput from 'components/operators/snippets/wg-exit-policy-install-output.mdx';
import ExitPolicyStatusOutput from 'components/operators/snippets/wg-exit-policy-status-output.mdx';
<Callout type="info" emoji="️">
**In case you had used `network-tunnel-manager.sh` with the command `complete_networking_setup`, your WireGuard exit policy is already setup. You can test it in the next chapter.**
</ Callout>
Nym Node running as Exit Gateway has contains multiple modules, one of them is Nym Network Requester(NR), routing TCP traffic to the internet. To make sure that the node is not just an open proxy, NR checks agains [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) following these conditions (in this exact order):
1. Do we explicitly block those IP addresses regardless of ports?
2. Do we allow those specific ports regardless of IPs?
3. Do block EVERYTHING else!
The exit policy is same for all NRs, the content is shaped by an offchain governance of Nym Node operators on our [forum](https://forum.nym.com/t/poll-a-new-nym-exit-policy-for-exit-gateways-and-the-nym-mixnet-is-inbound/464).
There is a caveat though. NR is only routing TCP streams and therefore any other type of routing than Mixnet 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.
**For all routing configuration we provide one tool [`network-tunnel-manager.sh` (NTM)](https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh). This tool manages WireGuard exit policy as well.**
In case you haven't run `network-tunnel-manager.sh` with the command `complete_networking_setup` you need to use NTM for WireGuard exit policy configuration.
**Folow these steps**
<Callout type="warning" emoji="⚠️">
**Run the following steps as root!**
</ Callout>
<Steps>
###### 1. Download `network-tunnel-manager.sh`, make executable and run with `--help` command:
```sh
curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/network-tunnel-manager.sh -o network-tunnel-manager.sh && \
chmod +x network-tunnel-manager.sh && \
./network-tunnel-manager.sh --help
```
###### 2. Install exit policy
- Clear old rules and configure new ones:
```sh
./network-tunnel-manager.sh exit_policy_clear
./network-tunnel-manager.sh exit_policy_install
```
- The output should look like this:
<AccordionTemplate name="Cosole output">
<ExitPolicyInstallOutput />
</ AccordionTemplate>
###### 3. Check status of your configuration
```sh
./network-tunnel-manager.sh exit_policy_status
```
- The output should look like this:
<AccordionTemplate name="Cosole output">
<ExitPolicyStatusOutput />
</ AccordionTemplate>
</ Steps>
Now your WireGuard routing (2-hop) should have same rotuing permissions like [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) used on 5-hop (Mixnet) mode of NymVPN.
@@ -1,39 +0,0 @@
import { Tabs } from 'nextra/components';
import { Steps } from 'nextra/components';
import ExitPolicyTestServer from 'components/operators/snippets/wg-exit-policy-testing-from-server.mdx';
import ExitPolicyTestOutside from 'components/operators/snippets/wg-exit-policy-testing-from-outside.mdx';
**For all routing configuration we provide one tool [`network-tunnel-manager.sh` (NTM)](https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh). This tool manages WireGuard tests all configurations, including WireGuard exit policy as well.**
You can use NTM to validate the application of the IP tables routes on your `nym-node` by checking it from the server side as well as from the outside.
<div>
<Tabs items={[
<strong>From the server</strong>,
<strong>From the outside - using NymVPN</strong>
]} defaultIndex={0}>
<Tabs.Tab><ExitPolicyTestServer /></Tabs.Tab>
<Tabs.Tab><ExitPolicyTestOutside /></Tabs.Tab>
</Tabs>
</div>
If all works , your node has successfully implemented WireGuard exit policy with the same routing permissions like [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) used on 5-hop (Mixnet) for TCP routing.
@@ -1,29 +1,9 @@
import { Steps } from 'nextra/components';
Here are a few ways you can ensre your WireGuard exit policy is working correctly from the outside.
<Steps>
###### 1. Using NymVPN
- Connect to NymVPN and use your node as an Exit Gateway in dVPN (2-hop) mode
- While connected to NymVPN, navigate to [`portquiz.net:12345`](http://portquiz.net:12345) and see if you can load the page
- It shouldn't load, but if you navigate to some of the accepted ports, like[`portquiz.net:443`](http://portquiz.net:443) it should all work
###### 2. Testing from your local terminal
- Install these dependencies on your local machine:
```shell
sudo apt install tcpdump
sudo tcpdump -i nymwg -n
```
- Connect to [NymVPN](https://nym.com) and select your node as an Exit Gateway
- Connect to [NymVPN](https://nym.com) and select your node as an Exit Gateway (after running the exit policy [manager script](#wireguard-exit-policy-configuration))
- Run the `tcpdump` command before registering
- Have the output of the `echo $BLOCKED_IP` from your node and try to go into your browser or a registered client and try to connect - It should not resolve
</ Steps>
- Have the output of the `echo $BLOCKED_IP` from your node and try to go into your browser or a registered client and try to connect - It will not resolve.
@@ -1,39 +1,11 @@
import { Steps } from 'nextra/components';
import { AccordionTemplate } from 'components/accordion-template.tsx';
import ExitPolicyTestOutput from 'components/operators/snippets/wg-exit-policy-test-output.mdx';
<Steps>
###### 1. Make sure to have the latest NTM:
```sh
curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/network-tunnel-manager.sh -o network-tunnel-manager.sh && \
chmod +x network-tunnel-manager.sh && \
./network-tunnel-manager.sh --help
```
###### 2. Run tests with NTM
```sh
./network-tunnel-manager.sh exit_policy_test_connectivity
./network-tunnel-manager.sh exit_policy_tests
```
- The output should look like this:
<AccordionTemplate name="Cosole output">
<ExitPolicyTestOutput />
</ AccordionTemplate>
###### 3. Optionally you can do some manual sanity checks
- Run this command to define variable `BLOCKED_IP` and try to `ping` it:
Run this command to define variable `BLOCKED_IP` and try to `ping` it:
```shell
BLOCKED_IP=$(grep "ExitPolicy reject" /etc/nym/exit-policy.txt | head -1 | sed -E 's/ExitPolicy reject ([^:]+):.*/\1/' | sed 's/\/.*$//')
ping -c 3 $BLOCKED_IP
```
- You should see `100% packet loss` as an outcome.
You should see `100% packet loss` as an outcome.
```shell
telnet $BLOCKED_IP 80
```
- You should see `telnet: Unable to connect to remote host: Connection timed out`.
</ Steps>
You should see `telnet: Unable to connect to remote host: Connection timed out`.
@@ -5,7 +5,7 @@
},
"mixmining_reserve": {
"denom": "unym",
"amount": "178754510529387"
"amount": "180875972213757"
},
"vesting_tokens": {
"denom": "unym",
@@ -13,6 +13,6 @@
},
"circulating_supply": {
"denom": "unym",
"amount": "821245489470613"
"amount": "819124027786243"
}
}
@@ -1 +1 @@
821_245_489
819_124_027
@@ -1 +1 @@
60_303_169
60_147_392
@@ -1 +1 @@
60_303_168
60_147_391
@@ -1,7 +1,7 @@
| **Item** | **Description** | **Amount in NYM** |
|:-------------------|:------------------------------------------------------|--------------------:|
| Total Supply | Maximum amount of NYM token in existence | 1_000_000_000 |
| Mixmining Reserve | Tokens releasing for operators rewards | 178_754_510 |
| Mixmining Reserve | Tokens releasing for operators rewards | 180_875_972 |
| Vesting Tokens | Tokens locked outside of cicrulation for future claim | 0 |
| Circulating Supply | Amount of unlocked tokens | 821_245_489 |
| Stake Saturation | Optimal size of node self-bond + delegation | 251_263 |
| Circulating Supply | Amount of unlocked tokens | 819_124_027 |
| Stake Saturation | Optimal size of node self-bond + delegation | 250_614 |
@@ -1,10 +1,10 @@
{
"interval": {
"reward_pool": "178754510529387.687865580967403579",
"staking_supply": "60303168385204.800235781003503301",
"reward_pool": "180875972213757.135840914936141948",
"staking_supply": "60147391744900.170789956043539529",
"staking_supply_scale_factor": "0.07342892",
"epoch_reward_budget": "4965403070.260769107377249094",
"stake_saturation_point": "251263201605.02000098242084793",
"epoch_reward_budget": "5024332561.493253773358748226",
"stake_saturation_point": "250614132270.417378291483514748",
"sybil_resistance": "0.3",
"active_set_work_factor": "10",
"interval_pool_emission": "0.02"
@@ -1 +1 @@
Thursday, November 20th 2025, 12:33:19 UTC
Tuesday, October 14th 2025, 11:34:14 UTC
@@ -11,7 +11,7 @@ options:
--no_routing_history Display node stats without routing history
--no_verloc_metrics Display node stats without verloc metrics
-m, --markdown Display results in markdown format
-o [OUTPUT], --output [OUTPUT]
-o, --output [OUTPUT]
Save results to file (in current dir or supply with
path without filename)
```
@@ -18,23 +18,23 @@
| [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) | USA, DE, UK, ESP, FR | nan | No | 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) | USA, NL, DE, UK, CA, SG, JP, AUS, HK | nan | No | KYC mandatory | 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 | 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, USA, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL | nan | 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, USA, 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 |
| [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) | USA, UK, Brazil, Japan, Russia, South Africa and many more | Yes | nan | 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 |
@@ -1,12 +0,0 @@
export const Green = ({ children }) => (
<span style={{ color: '#22C55E', fontWeight: 600 }}>{children}</span>
);
export const Yellow = ({ children }) => (
<span style={{ color: '#FACC15', fontWeight: 600 }}>{children}</span>
);
export const Red = ({ children }) => (
<span style={{ color: '#EF4444', fontWeight: 600 }}>{children}</span>
);
export const Gray = ({ children }) => (
<span style={{ color: '#6B7280', fontWeight: 600 }}>{children}</span>
);
+5 -5
View File
@@ -21,11 +21,11 @@
[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, USA, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL",,No,,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, USA, 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),"USA, DE, UK, ESP, FR",,No,,07/2025
[Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134),"USA, NL, DE, UK, CA, SG, JP, AUS, HK",,No,KYC mandatory,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
@@ -35,7 +35,7 @@
[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),"USA, UK, Brazil, Japan, Russia, South Africa and many more",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
1 **ISP** **Locations** **Public IPv6** **Crypto Payments** **Comments** **Last Updated**
21 [Lowendbox](https://lowendbox.com/category/dedicated-servers) Just an aggregator with good offers 07/2025
22 [Thundervm](https://thundervm.com/en/hosting/dedicated-server) USA, UK, France, Italy, Switzerland, Netherlands Yes 07/2025
23 [OVH](https://us.ovhcloud.com/bare-metal/rise/rise-3/) USA, DE, FR, UK, PL, CA No Not all locations always available 07/2025
24 [Mebilcom](https://www.melbicom.net/dedicatedserver/) NL, USA, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL NL, US, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL No 07/2025
25 [Servermania](https://www.servermania.com/dedicated-servers-hosting.htm) USA, Canada No 07/2025
26 [Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6) PL, FR, NL, UA, USA, 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 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
27 [Ionos](https://www.ionos.com/servers/amd-servers) USA, DE, UK, ESP, FR US, DE, UK, ESP, FR No 07/2025
28 [Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134) USA, NL, DE, UK, CA, SG, JP, AUS, HK US, NL, DE, UK, CA, SG, JP, AUS, HK No KYC mandatory 07/2025
29 [M247](https://m247.com/eu/services/host/dedicated-servers/) UK, Austria, Br, Sw, Jp, Poland, Fr, USA, Netherlands Yes No 07/2025
30 [Hostroyale](https://hostroyale.com/hosting/dedicated-server/) Various countries with different pricing Yes 07/2025
31 [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
35 [Colocall](https://www.colocall.net/) Ukraine Yes 07/2025
36 [Incognet](https://incognet.io/kansas-city-dedicated-servers) USA, Netherlands Yes 07/2025
37 [FranTech](https://my.frantech.ca) USA Yes 07/2025
38 [Psychz](https://www.psychz.net) USA, UK, Brazil, Japan, Russia, South Africa and many more US, UK, Brazil, Japan, Russia, South Africa and many more Yes 07/2025
39 [Fsit](https://www.fsit.com/server/vps-vserver-kvm) Swiss Yes Yes 07/2025
40 [NiceVPS](https://nicevps.net/) Netherlands Yes 07/2025
41 [Dataclub](https://www.dataclub.eu/) Latvia, Sweden, Netherlands Yes 07/2027
+8 -8
View File
@@ -1108,22 +1108,22 @@ const config = {
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
connect-src 'self' wss: https://github.com *.vercel.app *.nymtech.net *.nymvpn.com *.nymte.ch *.nyx.network *.nym.com https://nym.com nymvpn.com https://nymvpn.com *.nymtech.cc;
connect-src 'self' https://github.com *.vercel.app *.nymtech.net *.nymvpn.com *.nymte.ch *.nyx.network *.nym.com https://nym.com nymvpn.com https://nymvpn.com *.nymtech.cc;
frame-src 'self' https://vercel.live *.vercel.app *.nym.com https://nym.com;
worker-src 'self' blob: https://vercel.live *.vercel.app *.nym.com https://nym.com;
`;
return [
{
source: "/(.*)",
source: '/(.*)',
headers: [
{
key: "Content-Security-Policy",
key: 'Content-Security-Policy',
value: csp.replace(/\s{2,}/g, " ").trim(),
},
],
},
];
},
}
]
}
]
}
};
module.exports = config;
+2 -2
View File
@@ -38,8 +38,8 @@
"@nextui-org/accordion": "^2.0.40",
"@nextui-org/react": "^2.4.8",
"@nymproject/contract-clients": ">=1.2.4-rc.2 || ^1",
"@nymproject/mix-fetch-full-fat": ">=1.5.1-rc.0 || ^1.4.1",
"@nymproject/sdk-full-fat": ">=1.5.1-rc.0 || ^1.4.1",
"@nymproject/mix-fetch-full-fat": ">=1.2.4-rc.2 || ^1",
"@nymproject/sdk-full-fat": ">=1.2.4-rc.2 || ^1",
"@redocly/cli": "^1.25.15",
"@types/mdx": "^2.0.13",
"chain-registry": "^1.19.0",
@@ -1,4 +1,16 @@
# Introduction
This guide contains information about the various TypeScript SDK modules that facilitate interaction with different components of the Nym stack: the Nym mixnet & the Nyx blockchain.
import { Callout } from 'nextra/components';
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
Welcome to the documentation for Nym's TypeScript SDK!
This guide contains information about the various TypeScript SDK modules that facilitate interaction with different components of the Nym stack, including the Nym mixnet, the Nyx blockchain, and Coconut credentials.
@@ -1,6 +1,14 @@
# TS SDK FAQ
import { Callout } from 'nextra/components'
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
## Why and when does the mixnet client complain about insufficient topology?
It will in one of the following cases:
@@ -2,6 +2,14 @@
import { Callout } from 'nextra/components';
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
You might need some help bundling packages from the Nym Typescript SDK into your package.
Here are some things that could go wrong:
@@ -2,6 +2,14 @@ import { Callout } from 'nextra/components';
# Troubleshooting bundling with ESbuild
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
If you've been following the steps outlined in the Examples section, your development environment should be configured as follows:
#### Environment Setup
@@ -3,6 +3,14 @@ import { Callout } from 'nextra/components';
# Troubleshooting bundling with Webpack
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
## Webpack > 5 ESM
For any project using Webpack, you´ll need the following rule in your `webpack.config.js` above version 5:
@@ -2,6 +2,14 @@ import { Callout } from 'nextra/components'
# Cosmos Kit
<Callout type="error">
The TypeScript SDK is currently not avaliable: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
The wonderful people of Cosmology have made some [fantastic components](https://cosmoskit.com/) that can be used with
Nym. These include:
@@ -1,6 +1,13 @@
import { Callout } from 'nextra/components';
# `mixFetch`
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
An easy way to secure parts or all of your web app is to replace calls to [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) with `mixFetch`:
@@ -1,6 +1,13 @@
import { Callout } from 'nextra/components'
# Mixnet Client
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
As you know by now, in order to send or receive messages over the mixnet, you'll need to use the [`SDK Client`](https://www.npmjs.com/package/@nymproject/sdk), which will allow you to create apps that can use the Nym mixnet and Coconut credentials.
This client is message based - it can only send a one-way message to another client's address.
@@ -1,7 +1,13 @@
import { Callout } from 'nextra/components'
# Nym Smart Contract Clients
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
As previously mentioned, to query or execute on any of the Nym contracts, you'll need to use one of the [`Contract Clients`](https://www.npmjs.com/package/@nymproject/contract-clients), which contains read-only query and signing clients for all of Nym's smart contracts.
##### Contract Clients list
@@ -4,6 +4,14 @@ import { Callout } from 'nextra/components'
The different modules in the Typescript SDK allow developers to start building browser-based applications quickly. Simply import the SDK module of your choice depending on the component from the Nym architecture you want to use into your code via NPM, as you would any other TypeScript library.
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
<Callout type="info" emoji="️">
Other than the `Contract Clients`, SDK modules come in four different flavours (ESM, CJS and full-fat for ESM and CJS).
This documentation focuses on examples using the `full-fat` versions.
@@ -4,6 +4,15 @@ import { NPMLink } from '../../../components/npm';
## SDK overview
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
The Typescript SDK allows developers to start building browser-based Nym-based applications quickly, by simply importing the SDK modules into their code via NPM as they would any other Typescript library.
Currently developers can use different packages from the Typescript SDK to run the following entirely in browser:
@@ -6,6 +6,14 @@ import FormattedCosmoskitExampleCode from '../../../../code-examples/sdk/typescr
import { Callout } from 'nextra/components'
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
Below is an example that uses [CosmosKit](https://cosmoskit.com/) to connect and sign a fake transaction with your [Keplr wallet](https://www.keplr.app/) or
[Ledger hardware wallet](https://www.ledger.com/) to this page:
@@ -6,6 +6,14 @@ import FormattedMixFetchExampleCode from '../../../../code-examples/sdk/typescri
import { Callout } from 'nextra/components'
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
@@ -7,6 +7,14 @@ import { Callout } from 'nextra/components'
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
The Nym Mixnet contract keeps a directory of all mixnodes that can be used to mix traffic.
Here is a live example of querying the Mixnet Contract for a paged list of mixnodes:
@@ -5,6 +5,13 @@ import Box from '@mui/material/Box';
import FormattedTrafficExampleCode from '../../../../code-examples/sdk/typescript/traffic-example-code.mdx';
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
Use this tool to experiment with the mixnet: send and receive messages!
<Traffic />
@@ -13,6 +13,14 @@ import FormattedWalletDelegationsCode from '../../../../code-examples/sdk/typesc
import { Callout } from 'nextra/components'
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
Here's a small wallet example using testnet for you to test out!
<WalletContextProvider>
@@ -3,6 +3,14 @@ import { Callout } from 'nextra/components'
## MixFetch
<Callout type="error">
The TypeScript SDK is currently not avaliable to use: a network upgrade elsewhere has caused a problem which is not currently fixed. TS SDK Clients are not able to connect to the network.
When the issue is resolved, this will be reflected in the documentation.
Thanks for your patience!
</Callout>
Use the [`mixFetch`](https://www.npmjs.com/package/@nymproject/mix-fetch) package as a drop-in replacement for `fetch`to send HTTP requests over the Nym mixnet:
```ts
@@ -6,7 +6,6 @@
"sandbox": "Sandbox Testnet",
"binaries": "Binaries",
"nodes": "Nodes & Validators Guides",
"performance-and-testing": "Performance Measurement",
"tools": "Tools",
"troubleshooting": "Troubleshooting",
"tokenomics": "Tokenomics",
@@ -49,90 +49,6 @@ This page displays a full list of all the changes during our release cycle from
<VarInfo />
## `v2025.19-kase`
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.19-kase)
- [`nym-node`](nodes/nym-node.mdx) version `1.20.0`
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2025-10-30T12:43:37.933354749Z
Build Version: 1.20.0
Commit SHA: 75a6d3426bd18dca600ad1cfa39b0a3c4f319c69
Commit Date: 2025-10-30T11:59:32.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.88.0
rustc Channel: stable
cargo Profile: release
```
### Operators Updates & Tools
<Callout type="info" emoji="️">
**When this platform release becomes latest, we would like to ask operators ruuning any Gateway mode of `nym-node`, to use new version of [QUIC brige deployment tool](https://github.com/nymtech/nym/blob/develop/scripts/nym-node-setup/quic_bridge_deployment.sh) and install QUIC `nym-bridge` on their server, following [these steps](#quic-transport-bridge-deployment).**
</Callout>
Alongside this platform release we are happy to introduce several improvements and new tools for node operators.
- [Updated version of QUIC brige deployment tool](https://github.com/nymtech/nym/blob/develop/scripts/nym-node-setup/quic_bridge_deployment.sh), **if you run a `nym-node` in any Gateway mode, please install QUIC on your server, following [these steps](#quic-transport-bridge-deployment)**
- [New **Nym Node Status Dashboard**](https://node-status.nym.com)
- [New **Harbourmaster** aka ***Nym Node Status Observatory***](https://harbourmaster.nymtech.net)
### Features
- [Propagate cancel token to mixnet client](https://github.com/nymtech/nym/pull/6105): Ensures cancellation token propagation to mixnet client
- [[DOCs/operators] QUIC deployment script & docs](https://github.com/nymtech/nym/pull/6098): Script and documentation for QUIC deployment, referencing `nym-bridges` repository
- [Move gateway probe to monorepo (Rust edition 2024)](https://github.com/nymtech/nym/pull/6094): Moves `nym-gateway-probe` and related packages into monorepo, updates to Rust 2024 edition
- [Expose reference to Mnemonic from `DirectSecp256k1HdWallet`](https://github.com/nymtech/nym/pull/6083): Adds safer accessors for mnemonic references and deprecates unsafe cloning
### Bugfix
- [Cherry pick - request #6143 from nymtech/bugfix/mix-tx-closed-v2](https://github.com/nymtech/nym/pull/6153): Add circuit breaker
<AccordionTemplate name={<TestingSteps/>}>
**Summary:**
- Network-requester started successfully
- SOCKS5 client started successfully
- Traffic was proxied through the mixnet
- Shutdown was clean
- No 'channel closed (outside of shutdown!)' errors
</AccordionTemplate>
- [`nym-credential-proxy` query params parsing regression](https://github.com/nymtech/nym/pull/6121): Fix query deserialization issue with `serde_urlencoded` breaking compatibility with VPN API
- [Revert some dep updates introduced in #6043](https://github.com/nymtech/nym/pull/6120): Revert dependency updates that broke ANSI escape characters within tracing output
- [Skip IPv6 metadata endpoint request](https://github.com/nymtech/nym/pull/6118): Skip querying IPv4-only metadata endpoints during IPv6 probing tests
- [Revert "Propagate cancel token to mixnet client"](https://github.com/nymtech/nym/pull/6115): Reverts earlier change due to premature mixnet exit issues
- [Retrieve and update ticketbook in the same query](https://github.com/nymtech/nym/pull/6101): Fix concurrency issue with multiple agents retrieving ticketbooks simultaneously
- [Include network name in default gateway probe config path](https://github.com/nymtech/nym/pull/6100): Prevents reuse of credentials across different networks
- [Incompatibility fixes](https://github.com/nymtech/nym/pull/6099): Fixes several incompatibilities, including initialization and build mismatches
- [Testnet manager `02sql` migration](https://github.com/nymtech/nym/pull/6096): Fix invalid FK constraint blocking SQL migration
- [Use custom topology provider for list of init gateways](https://github.com/nymtech/nym/pull/6092): Fixes SDK bug where clients ignored custom topology provider on registration
- [Fix `WASM` client + build commands](https://github.com/nymtech/nym/pull/6043): Fixes WASM client hang and runtime time-related issues; improves internal dev testing stability
### Refactors & Maintenance
- [Update to no longer use 1mb files](https://github.com/nymtech/nym/pull/6117)
- [Restore pending DKG contract state migration](https://github.com/nymtech/nym/pull/6116)
- [Update `dirs` to `6.0`](https://github.com/nymtech/nym/pull/6109): Minor dependency update, safe for compatibility
## `v2025.18-jarlsberg`
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.18-jarlsberg)
@@ -2881,7 +2797,7 @@ cargo Profile: release
<Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](performance-and-testing/gateway-probe).
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](nodes/performance-and-testing/gateway-probe).
</Callout>
### Operators Updates & Tools
@@ -40,9 +40,6 @@ If you want to explore kickstart options and examples, learn how to integrate wi
* [Validators](nodes/validator-setup.mdx)
* [Nym API Setup](nodes/validator-setup/nym-api.mdx)
**Performance Monitoring**
* [Performance, probe and measurements](performance-and-testing)
**Tokenomics, rewards and roadmap**
* [General tokenomics page](tokenomics.mdx)
* [Nym Node rewards page](tokenomics/mixnet-rewards.mdx)
@@ -2,5 +2,6 @@
"preliminary-steps": "Preliminary Steps",
"nym-node": "Nym Node",
"validator-setup": "Nyx Validator Setup",
"maintenance": "Maintenance"
"maintenance": "Maintenance",
"performance-and-testing": "Performance Monitoring & Testing"
}
@@ -55,7 +55,7 @@ journalctl -f -u nym-node.service
<Callout type="warning" emoji="⚠️">
After changes coming along with `v2024.13-magura` (`nym-node v1.1.10`), Nym Explorer is no longer picking all values correctly. Instead of fixing this outdated explorer, we are working on a new one, coming out soon.
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](/operators/performance-and-testing/gateway-probe).
[Nym Harbourmaster](https://harbourmaster.nymtech.net) has cache of 90min, expect your values to be updated with delay. We are aware of some issues with Nym Harbourmaster and working hard to resolve them in the upcoming explorer v2. To check your routing values in real time, you can use [`nym-gateway-probe`](../performance-and-testing/gateway-probe).
</Callout>
## Validator Upgrade
@@ -28,7 +28,7 @@ Do not bond your node to the API if the previous steps weren't finished. Bad con
Any new bonded node will provide only the bare minimum information: host, identity key and optionally custom port of its HTTP API - we highly recommend to set that one up to `8080`. Everything else will be discovered via the self-described API for maximum flexibility. This also includes the sphinx key, meaning if the API is not exposed, the node will be unable to route any traffic.
**Every operator has to make sure that their nodes [self-described endpoint works](/operators/performance-and-testing#functionality--performance-check), otherwise the node will be un-routable and thus won't get any rewards!**
**Every operator has to make sure that their nodes [self-described endpoint works](../performance-and-testing#functionality--performance-check), otherwise the node will be un-routable and thus won't get any rewards!**
<Callout type="warning" emoji="⚠️">
**Reveal your menominc phrase only in areas out of surveillance of other people and never share it with others. Nym team will never ask you for your mnemonic phrase - in case you were asked by someone it's a scam, do *not* reply to it!**
@@ -79,7 +79,7 @@ If you are part of [Nym Delegation Program](https://delegations.explorenym.net)
- You will be asked to run a `sign` command with your `nym-node` - copy and paste the long signature as the value of `--contract-msg` and sing it on your VPS:
```sh
./nym-node sign --id <ID> --contract-msg <PAYLOAD_GENERATED_BY_THE_WALLET>
./nym-node sign --contract-msg <PAYLOAD_GENERATED_BY_THE_WALLET>
```
- Copy the resulting signature string and paste it into the wallet nodal, press `Next` and confirm the transaction:
@@ -3,11 +3,34 @@ import { Tabs } from 'nextra/components';
import { VarInfo } from 'components/variable-info.tsx';
import { Steps } from 'nextra/components';
import { AccordionTemplate } from 'components/accordion-template.tsx';
import WGExitPolicyConf from 'components/operators/snippets/wg-exit-policy-conf.mdx';
import WGExitPolicyTest from 'components/operators/snippets/wg-exit-policy-test.mdx';
import RoutingConf from 'components/operators/snippets/routing-conf.mdx';
import ExitPolicyInstallOutput from 'components/operators/snippets/wg-exit-policy-install-output.mdx';
import ExitPolicyStatusOutput from 'components/operators/snippets/wg-exit-policy-status-output.mdx';
import ExitPolicyTestOutput from 'components/operators/snippets/wg-exit-policy-test-output.mdx';
import ExitPolicyTestServer from 'components/operators/snippets/wg-exit-policy-testing-from-server.mdx';
import ExitPolicyTestOutside from 'components/operators/snippets/wg-exit-policy-testing-from-outside.mdx';
import QuicDeploymentSteps from 'components/operators/snippets/quic-bridge-deployment-script-setup.mdx';
export const ManagerIPOutput = () => (
<div>
Correct <code>./network_tunnel_manager.sh fetch_and_display_ipv6</code> output
</div>
);
export const ManagerTablesOutput = () => (
<div>
Correct <code>./network_tunnel_manager.sh check_nymtun_iptables</code> output
</div>
);
export const ShowTun = () => (
<div>
Correct <code>ip addr show nymtun0</code> output
</div>
);
# Nym Node Configuration
<VarInfo />
@@ -199,11 +222,9 @@ This lets your operating system know it's ok to reload the service configuration
</Steps>
## Routing Configuration
## Connectivity Test and Configuration
<RoutingConf />
### Quick IPv6 Check
During our ongoing testing events we found out, that after introducing IP Packet Router (IPR) and [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) on embedded Network Requester (NR) by default, only a fragment of Gateways routes correctly through IPv4 and IPv6. We built a useful monitor to check out your Gateway (`nym-node --mode exit-gateway`) at [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
IPv6 routing is not only a case for gateways. Imagine a rare occasion when you run a `mixnode` without IPv6 enabled and a client will sent IPv6 packets through the Mixnet through such route:
```ascii
@@ -211,6 +232,19 @@ IPv6 routing is not only a case for gateways. Imagine a rare occasion when you r
```
In this (unusual) case your `mixnode` will not be able to route the packets. The node will drop the packets and its performance would go down. For that reason it's beneficial to have IPv6 enabled when running a `mixnode` functionality.
<Callout>
We recommend operators to configure their `nym-node` with the full routing configuration.
However, most of the time the packets sent through the Mixnet are IPv4 based. The IPv6 packets are still pretty rare and therefore it's not mandatory from operational point of view to have this configuration implemented if you running only `mixnode` mode.
If you preparing to run a `nym-node` with all modes enabled in the future, this setup is required.
</Callout>
<Callout type="info" emoji="️">
For everyone participating in Delegation Program or Service Grant program, this setup is a requirement!
</Callout>
### Quick IPv6 Check
You can always check IPv6 address and connectivity by using some of these methods:
<br />
@@ -239,13 +273,267 @@ telnet -6 ipv6.telnetmyip.com
Make sure to keep your IPv4 address enabled while setting up IPv6, as the majority of routing goes through that one!
</Callout>
### Routing Configuration
While we're working on Rust implementation to have these settings as a part of the binary build, to solve these connectivity requirements in the meantime we wrote a script [`network_tunnel_manager.sh`](https://github.com/nymtech/nym/blob/develop/scripts/network_tunnel_manager.sh) to support operators to configure their servers and address all the connectivity requirements.
Networking configuration across different ISPs and various operation systems does not have a generic solution. If the provided configuration setup doesn't solve your problem check out [IPv6 troubleshooting](../../troubleshooting/vps-isp.mdx#ipv6-troubleshooting) page. Be aware that you may have to do more research, customised adjustments or contact your ISP to change settings for your VPS.
The `nymtun0` interface is dynamically managed by the `exit-gateway` service. When the service is stopped, `nymtun0` disappears, and when started, `nymtun0` is recreated.
The `nymwg` interface is used for creating a secure wireguard tunnel as part of the Nym Network configuration. Similar to `nymtun0`, the script manages iptables rules specific to `nymwg` to ensure proper routing and forwarding through the wireguard tunnel. The `nymwg` interface needs to be correctly configured and active for the related commands to function properly. This includes applying or removing iptables rules and running connectivity tests through the `nymwg` tunnel.
The script should be used in a context where `nym-node` is running to fully utilise its capabilities, particularly for fetching IPv6 addresses or applying network rules that depend on the `nymtun0` and `nymwg` interfaces and to establish a WireGuard tunnel.
**Before starting with the following configuration, make sure you have the [latest `nym-node` binary](https://github.com/nymtech/nym/releases) installed and your [VPS setup](../preliminary-steps/vps-setup.mdx) finished properly!**
<Steps>
###### 1. Download `network_tunnel_manager.sh`, make executable and run:
```sh
curl -L https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/network_tunnel_manager.sh -o network_tunnel_manager.sh && \
chmod +x network_tunnel_manager.sh && \
./network_tunnel_manager.sh
```
###### 2. Make sure your `nym-node` service is up and running and bond
- **If you setting up a new node and not upgrading an existing one, keep it running and [bond](bonding.mdx) your node now**. Then come back here and follow the rest of the configuration.
<Callout type="warning" emoji="⚠️">
**Run the following steps as root or with `sudo` prefix!**
</Callout>
###### 3. Setup IP tables rules
- Delete IP tables rules for IPv4 and IPv6 and apply new ones:
```sh
./network_tunnel_manager.sh remove_duplicate_rules nymtun0
./network_tunnel_manager.sh apply_iptables_rules
```
- The process may prompt you if you want to save current IPv4 and IPv6 rules, choose yes.
![](/images/operators/ip_table_prompt.png)
- At this point you should see a `global ipv6` address.
```sh
./network_tunnel_manager.sh fetch_and_display_ipv6
```
<br />
<AccordionTemplate name={<ManagerTablesOutput/>}>
```sh
iptables-persistent is already installed.
Using IPv6 address: 2001:db8:a160::1/112 #the address will be different for you
operation fetch_ipv6_address_nym_tun completed successfully.
```
</AccordionTemplate>
###### 4. Check Nymtun IP tables:
```sh
./network_tunnel_manager.sh check_nymtun_iptables
```
- If there's no process running it wouldn't return anything.
- In case you see `nymtun0` but not active, this is probably because you are setting up a new (never bonded) node and not upgrading an existing one.
<br />
<AccordionTemplate name={<ManagerIPOutput/>}>
```sh
iptables-persistent is already installed.
network Device: eth0
---------------------------------------
inspecting IPv4 firewall rules...
Chain FORWARD (policy DROP 0 packets, 0 bytes)
0 0 ufw-reject-forward all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- nymtun0 eth0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- eth0 nymtun0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT all -- nymtun0 eth0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- eth0 nymtun0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT all -- nymtun0 eth0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- eth0 nymtun0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
---------------------------------------
inspecting IPv6 firewall rules...
Chain FORWARD (policy DROP 0 packets, 0 bytes)
0 0 ufw6-reject-forward all * * ::/0 ::/0
0 0 ACCEPT all eth0 nymtun0 ::/0 ::/0 state RELATED,ESTABLISHED
0 0 ACCEPT all nymtun0 eth0 ::/0 ::/0
0 0 ACCEPT all eth0 nymtun0 ::/0 ::/0 state RELATED,ESTABLISHED
0 0 ACCEPT all nymtun0 eth0 ::/0 ::/0
0 0 ACCEPT all eth0 nymtun0 ::/0 ::/0 state RELATED,ESTABLISHED
0 0 ACCEPT all nymtun0 eth0 ::/0 ::/0
operation check_nymtun_iptables completed successfully.
```
</AccordionTemplate>
###### 5. Remove old and apply new rules for wireguad routing
```sh
/network_tunnel_manager.sh remove_duplicate_rules nymwg
./network_tunnel_manager.sh apply_iptables_rules_wg
```
###### 6. Apply rules to configure DNS routing and allow ICMP piung test for node probing (network testing)
```sh
./network_tunnel_manager.sh configure_dns_and_icmp_wg
```
###### 7. Adjust and validate IP forwarding
```sh
./network_tunnel_manager.sh adjust_ip_forwarding
./network_tunnel_manager.sh check_ipv6_ipv4_forwarding
```
###### 8. Check `nymtun0` interface and test routing configuration
```sh
ip addr show nymtun0
```
<br />
<AccordionTemplate name={<ShowTun/>}>
```sh
# your addresses will be different
8: nymtun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1420 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 10.0.0.1/16 scope global nymtun0
valid_lft forever preferred_lft forever
inet6 fc00::1/112 scope global
valid_lft forever preferred_lft forever
inet6 fe80::ad08:d167:5700:8c7c/64 scope link stable-privacy
valid_lft forever preferred_lft forever`
```
</AccordionTemplate>
- Validate your IPv6 and IPv4 networking by running a joke test via Mixnet:
```sh
./network_tunnel_manager.sh joke_through_the_mixnet
```
- Validate your tunneling by running a joke test via WG:
```sh
./network_tunnel_manager.sh joke_through_wg_tunnel
```
- **Note:** WireGuard will return only IPv4 joke, not IPv6. WG IPv6 is under development. Running IPR joke through the mixnet with `./network_tunnel_manager.sh joke_through_the_mixnet` should work with both IPv4 and IPv6!
###### 9. Enable wireguard
Now you can run your node with the `--wireguard-enabled true` flag or add it to your [systemd service config](#systemd). Restart your `nym-node` or [systemd](#2-following-steps-for-nym-nodes-running-as-systemd-service) service (recommended):
```sh
systemctl daemon-reload && service nym-node restart
```
- Optionally, you can check if the node is running correctly by monitoring the service logs:
```sh
journalctl -u nym-node.service -f -n 100
```
</Steps>
Make sure that you get the validation of all connectivity. If there are still any problems, please refer to [troubleshooting section](../../troubleshooting/vps-isp.mdx#incorrect-gateway-network-check).
## Wireguard Exit Policy Configuration
<WGExitPolicyConf />
Nym Node running as Exit Gateway has contains multiple modules, one of them is Nym Network Requester(NR), routing TCP traffic to the internet. To make sure that the node is not just an open proxy, NR checks agains [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) following these conditions (in this exact order):
1. Do we explicitly block those IP addresses regardless of ports?
2. Do we allow those specific ports regardless of IPs?
3. Do block EVERYTHING else!
The exit policy is same for all NRs, the content is shaped by an offchain governance of Nym Node operators on our [forum](https://forum.nym.com/t/poll-a-new-nym-exit-policy-for-exit-gateways-and-the-nym-mixnet-is-inbound/464).
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:**
<Steps>
###### 1. Download the scripts and make executable
- SSH to your node
- Create a folder `~/nym-binaries` and navigate there
```sh
mkdir $HOME/nym-binaries
cd $HOME/nym-binaries
```
- Download the scripts
```sh
wget https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh
wget https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/exit-policy-tests.sh
```
- Make executable
```sh
chmod +x wireguard-exit-policy-manager.sh exit-policy-tests.sh
```
###### 2. Install `wireguard-exit-policy-manager.sh`
```sh
./wireguard-exit-policy-manager.sh install
```
- The output should look like this:
<AccordionTemplate name="Cosole output">
<ExitPolicyInstallOutput />
</ AccordionTemplate>
###### 3. Run `wireguard-exit-policy-manager.sh`
```sh
./wireguard-exit-policy-manager.sh status
```
- The output should look like this:
<AccordionTemplate name="Cosole output">
<ExitPolicyStatusOutput />
</ AccordionTemplate>
###### 4. Test with `exit-policy-tests.sh`
```sh
./exit-policy-tests.sh
```
- The output should look like this:
<AccordionTemplate name="Cosole output">
<ExitPolicyTestOutput />
</ AccordionTemplate>
###### 5. In case of problems, you can clear the exit policy rule
```sh
./wireguard-exit-policy-manager.sh clear
./wireguard-exit-policy-manager.sh status
```
</ Steps>
Now your wireguart routing should have same rotuing permissions like [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) used on 5-hop (Mixnet) mode of NymVPN.
### Testing Wireguard Exit Policy
<WGExitPolicyTest />
You can validate the application of the IP tables routes on your `nym-node` by checking it from the server side as well as from the outside.
<div>
<Tabs items={[
<strong>From the server</strong>,
<strong>From the outside - using NymVPN</strong>
]} defaultIndex={0}>
<Tabs.Tab><ExitPolicyTestServer /></Tabs.Tab>
<Tabs.Tab><ExitPolicyTestOutside /></Tabs.Tab>
</Tabs>
</div>
Your node has successfully implemented wireguard exit policy with the same routing permissions like [Nym exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) used on 5-hop (Mixnet).
## QUIC Transport Bridge Deployment
@@ -21,13 +21,13 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2025-10-30T12:43:37.933354749Z
Build Version: 1.20.0
Commit SHA: 75a6d3426bd18dca600ad1cfa39b0a3c4f319c69
Commit Date: 2025-10-30T11:59:32.000000000+01:00
Commit Branch: HEAD
rustc Version: 1.88.0
rustc Channel: stable
Build Timestamp: 2025-10-15T09:04:32.043934599Z
Build Version: 1.19.0
Commit SHA: 2235a6e1477bea7368ee5443a298f544deb63504
Commit Date: 2025-10-15T10:22:16.000000000+02:00
Commit Branch: master
rustc Version: 1.92.0-nightly
rustc Channel: nightly
cargo Profile: release
```
@@ -184,7 +184,7 @@ Make sure to use `--deny-init` flag to prevent initialisation of a new node.
## Functionality & Performance Check
We have a chapter called [Performance Monitoring & Testing](/operators/performance-and-testing) including much more information and tooling. If you want to just quickly check your nodes performance, connectivity and much more, visit [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
We have a chapter called [Performance Monitoring & Testing](../performance-and-testing) including much more information and tooling. If you want to just quickly check your nodes performance, connectivity and much more, visit [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
For more information about available endpoints and their status, you can refer to:
```sh
@@ -1,37 +1,18 @@
import { Callout } from 'nextra/components';
import { Tabs } from 'nextra/components';
import { MyTab } from 'components/generic-tabs.tsx';
import { AccordionTemplate } from 'components/accordion-template.tsx';
import NodePerfWG from 'components/operators/snippets/node-perf-wg.mdx';
import NodePerfMixnet from 'components/operators/snippets/node-perf-mixnet.mdx';
# Performance Monitoring & Testing
As Nym developers constantly improve the software, the role of Node Operators is to keep their nodes up to date, monitor their performance and share feedback with the rest of the community and Nym team. Node performance measurements and [server monitoring](#monitoring) are an essential pillar of our common work.
Nym Mixnet has been running on mainnet for quite some time. There is still work to be done in order for the network to meet its full potential - mass adoption of privacy through fully distributed Mixnet.
Nym Network is routed either through the Mixnet (5-hop) or through Wireguard (2-hop). In all cases Nym node operators always employ only one binary called [`nym-node`](/operators/nodes/nym-node). Through provided arguments (or changes in the config file), `nym-node` can be utilised for different [functionalities](/operators/nodes/nym-node/setup#functionality-mode). However, once it's [registered to Nym Network](/operators/nodes/nym-node/bonding) it's by default available for Nym Mixnet not for Wireguard routing. Only nodes with Wireguard enabled, are also available for Wireguard routing. This creates a situation where every Wireguard enabled `nym-node` is required to have a solid performance score in Mixnet to begin with, but not every Mixnet routing `nym-node` must have Wireguard enabled.
Given this complexity, we divided the part below about perfromance calculation logic and node selection into two parallel tabs: Mixnet and Wireguard.
<div>
<Tabs items={[
<strong>Mixnet: Nodes Performance Calculation</strong>,
<strong>WireGuard: Gateways Performance Calculation</strong>,
]} defaultIndex="0">
<MyTab><NodePerfMixnet /></MyTab>
<MyTab><NodePerfWG /></MyTab>
</Tabs>
</div>
As developers we need to be constantly improving the software. Operators have as much important role, keep their nodes up to date, monitor their performance and share their feedback with the rest of the community and core developers.
Therefore [monitoring](#monitoring) and [testing](#testing) are essential pieces of our common work. We call out all Nym operators to join the efforts!
## Functionality & Performance Check
If you want to check your nodes performance, connectivity and much more, see some of the dashobards:
If you want to just quickly check your nodes performance, connectivity and much more, visit [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
- [Node Status dashboard](https://node-status.nym.com/dvpn): Shows latest probe results on one board
- [Nym Node Status Observatory](https://harbourmaster.nymtech.net): New version of a good old Nym Harbourmaster, allowing operators preview stats of each node
For more information about available endpoints and their status, you can refer to [`nymvpn.com/api/public/v1/directory/gateways`](https://nymvpn.com/api/public/v1/directory/gateways) or see directly self described endpoints of your node:
For more information about available endpoints and their status, you can refer to:
```sh
# sustitude <IPv4_ADDRESS> or <HOSTNAME> with the one corresponding to your node
# for http
@@ -42,7 +23,7 @@ http://<IPv4_ADDRESS>/api/v1/swagger/#/
# for reversed proxy/WSS
https://<HOSTNAME>/api/v1/swagger/#/
```
<AccordionTemplate name="✏️ Example: Accessing self-described endpoints">
For example to determine which mode your node is running, you can check the `:8080/api/v1/roles` endpoint:
```sh
# sustitude <IPv4_ADDRESS> or <HOSTNAME> with the one corresponding to your node
@@ -54,7 +35,6 @@ http://<IPv4_ADDRESS>/api/v1/roles
# for reversed proxy/WSS
https://<HOSTNAME>/api/v1/roles
```
</AccordionTemplate>
## Monitoring
@@ -64,7 +44,7 @@ There are multiple ways to monitor performance of nodes and the machines on whic
A list of different scripts, templates and guides for easier navigation:
* [`nym-gateway-probe`](performance-and-testing/gateway-probe.mdx) - a useful tool used under the hood of [Node Status Observatory](https://harbourmaster.nymtech.net)
* [`nym-gateway-probe`](perfomance-and-testing/gateway-probe.mdx) - a useful tool used under the hood of [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net)
* [Prometheus and Grafana](performance-and-testing/prometheus-grafana.mdx) self-hosted setup
* [Nym-node CPU cron service](https://gist.github.com/tommyv1987/97e939a7adf491333d686a8eaa68d4bd) - an easy bash script by Nym core developer [@tommy1987](https://gist.github.com/tommyv1987), designed to monitor a CPU usage of your node, running locally
* Nym's script [`prom_targets.py`](https://github.com/nymtech/nym/blob/develop/scripts/prom_targets.py) - a useful python program to request data from API and can be run on its own or plugged to more sophisticated flows
@@ -73,7 +53,7 @@ A list of different scripts, templates and guides for easier navigation:
For the purpose of the performance testing Nym core developers plan to run instances of Prometheus and Grafana connected to Node explorer in the house. The network overall key insights we seek from these tests are primarily internal. We're focused on pinpointing bottlenecks, capacity loads, and monitoring cpu usage on the nodes' machines.
{/* LEAVING THIS BIT FOR FUTURE F&F CASES:
## Testing
<Callout type="info" emoji="️">
@@ -93,4 +73,3 @@ We do testing in order to **understand and increase the overall quality of the N
7. Adjust rewarding based on the machine specs and server pricing
Visit [Nym Harbour Master](https://harbourmaster.nymtech.net/) monitoring page to monitor network components (nodes) performance.
*/}
@@ -1,5 +1,4 @@
{
{
"gateway-probe": "Gateway Probe",
"gateway-probe-details": "Gateway Probe Details",
"prometheus-grafana": "Prometheus & Grafana"
}
@@ -1,139 +0,0 @@
import { Callout, CalloutTitle, CalloutDescription } from 'nextra/components';
import { Steps } from 'nextra/components';
import { IPR } from 'components/operators/snippets/ipr.js';
import { ICMP } from 'components/operators/snippets/icmp.js';
# Gateway Probes Details & Contradictions
## Summary
A simplified explanation of node performance measuring is that the [Node Status API](https://node-status.nym.com) runs [gateway probes](/operators/performance-and-testing/gateway-probe) that connect to gateways to:
- Check the configuration of gateways
- Checks a list of capabilities (e.g. can route IPv4 traffic in mixnet mode)
- Checks a list of configuration (e.g. runs <abbr title={IPR}>IPR</abbr>, has exit policy)
- Acts like a user:
- Registers a mixnet client
- Registers a wireguard peer and tops up bandwidth with a zk-nym
- Sends <abbr title={ICMP}>ICMP</abbr> ping packets
- Downloads files
The results are collected and stored in the [Node Status API](https://node-status.nym.com) and can be also veiwed per node in [Node Status Observatory](https://harbourmaster.nymtech.net).
The [NymVPN API directory](https://nymvpn.com/api/public/v1/directory/gateways) cache uses the output of the gateway probes to calculate and display hints to users about the contention on each gateway and what they might expect if they use the gateway.
## Heisenbergs Uncertainty Principle for Gateways
> The uncertainty principle, also known as Heisenberg's indeterminacy principle, is a fundamental concept in quantum mechanics. It states that there is a limit to the precision with which certain pairs of physical properties, such as position and momentum, can be simultaneously known. In other words, the more accurately one property is measured, the less accurately the other property can be known.
>
> [https://en.wikipedia.org/wiki/Uncertainty\_principle](https://en.wikipedia.org/wiki/Uncertainty_principle)
The nodes in the Nym network are run by independent operators, so we can only know:
- What we see from the outside
- Probes can run tests like users
- Users can report performance from a Gateway (also includes the performance of the users internet connection and the speed of the host they are using)
- What we ask operators to report
- They can lie
- We cant check them
### Gateway Probe Generates a Stream to Fill Available Bandwidth on Gateway
The probe can check a Gateway periodically and can generate a traffic stream to use the remaining bandwidth:
![](/images/operators/performance/perceived-gw-dwl.png)
Depending on how busy the Gateway is with user streams, the probe will report different values for the available remaining bandwidth.
<Callout >
This is currently what we do with downloading a 10MB file
</Callout>
If the timing of the probe and user activity overlaps, then the “available bandwidth” remaining looks like this from the probes perspective:
![](/images/operators/performance/bandwith-probe-user-overlaps.png)
### Fully Utilised Gateways Will Have Low Availability
If a Gateway is popular, then there will not be a lot of room for the probes stream:
![](/images/operators/performance/popular-gw-probe.png)
The Gateway is doing a fine job is serving 92.5% of its available capacity to clients, and it only have 7.5Mbps available for another user.
<Callout>
Is that enough for a user? Only they can know!
</Callout>
### Can Another Netflix 4k Stream Fit?
Streaming Netflix at 4k takes anywhere from 11-15Mbps, so lets see what that looks like in a few cases:
###### “Yay, Netflix will fit most of the time”
![](/images/operators/performance/netflix-fits-bandwidth.png)
###### “Netflix is not going to fit”
![](/images/operators/performance/netflix-not-fit.png)
### Does Measuring Affect Users of the Gateway?
If we add heavy traffic streams to Gateways, what happens to other users?
Imagine there are 6 \* Netflix 4k users on a 100Mbps Gateway, let's look at different traffic stream scenarios:
###### Take the space of 2 streams
Two users will stall while we use their space:
![](/images/operators/performance/two-streams-space.png)
###### Scale the user space down by the test stream
Adjust the Gateway settings to force users into less bandwidth and make the test take priority:
*Sad users for 5 minutes*
![](/images/operators/performance/sad-users.png)
*Angry users for 5 minutes*
![](/images/operators/performance/angry-users.png)
*Furious users for 5 minutes*
![](/images/operators/performance/furious-users.png)
## Gateways, IaaS and Network Quotas
IaaS providers like Linode, AWS, etc and data centre providers have a mix of methods where they can cap or rate limit VMs or physical connections:
- **Fixed quota:** you can only use the outbound networking available, e.g. AWS limits to 100Mbps
![](/images/operators/performance/capped-bandwidth.png)
- **Fixed quota with burst:** you get a fixed outbound network rate, but can burst above it
- You pay penalties when you burst - many data centres and fixed lines do this
- You have a leaky bucket - you can only burst with your saved quota
![](/images/operators/performance/burst-charges.png)
- **Leaky bucket quota:** you start with a small quota that builds up to a maximum, but once you use your “saved quota” you drop down to the rate at which your quota comes in. Or sometimes no matter how much you pour into the bucket, only a fixed amount drains out the other side (see [https://en.wikipedia.org/wiki/Leaky\_bucket](https://en.wikipedia.org/wiki/Leaky_bucket))
![](/images/operators/performance/leaky-bucket.png)
This means that if there is a flexible quota, a gateway with high usage, can burn through its quota early in the monthly billing cycle leaving it rate limited for the majority of the month:
![](/images/operators/performance/vps-quota.png)
The cheaper the VPS, the more it is likely to be rate limited or have a leaky bucket quota that will appear to users as “unreliable”.
The best hosts will be ones with a fixed bandwidth that is capped by the hosting provider with network equipment, because:
- Users will scale with the available bandwidth
- No weird side effects of quotas
- Most predictable behaviour
- Will not be the cheapest, but will also not be the most expensive
Bursting sounds attractive, but could be financially crippling for operators.
@@ -148,7 +148,7 @@ Nym Mixnet is using an active set of chosen nodes. Currently <b>the [active set
The alorithm for selecting the nodes into the Rewarded set is in detail explained in the [Rewarded set selection logic part](tokenomics/mixnet-rewards#rewarded-set-selection).
</MyTab>
<MyTab>
In dVPN (2-hop) mode every node which meets the [performance criteria](tokenomics/mixnet-rewards#mixnet-node-performance-calculation), including wireguard and IPv6 routing tests, becomes eligible to take part in the network. Whether the node is working or not then depends on the NymVPN end users choise of the location or exact nodes selection.
In dVPN (2-hop) mode every node which meets the [performance criteria](tokenomics/mixnet-rewards#node-performance-calculation), including wireguard and IPv6 routing tests, becomes eligible to take part in the network. Whether the node is working or not then depends on the NymVPN end users choise of the location or exact nodes selection.
</MyTab>
</Tabs>
</div>

Some files were not shown because too many files have changed in this diff Show More