Compare commits

..

110 Commits

Author SHA1 Message Date
import this 340960d957 [DOCs/operators]: Release notes for v2025.19 kase (#6157)
* add release and operators notes

* bump up version

* fix location in csv to USA

* bump up stats

* typo fix
2025-10-31 09:02:13 +00:00
Mark Sinclair 554e446208 change migration and bump version 2025-10-31 09:02:13 +00:00
Mark Sinclair a6325b922a bump version to rc 2025-10-31 09:02:13 +00:00
Mark Sinclair fae4768b99 add tracing output 2025-10-31 09:02:13 +00:00
Mark Sinclair 2689a4dbd8 clippy 2025-10-31 09:02:13 +00:00
Mark Sinclair 02e40ccaef save custom_http_port to db 2025-10-31 09:02:13 +00:00
Mark Sinclair b216338364 allow NS API to run once for scraping for troubleshooting and debugging 2025-10-31 09:02:13 +00:00
Mark Sinclair 4a83bb9ba8 wip 2025-10-31 09:02:13 +00:00
Mark Sinclair 66ec3b037f ns-api: fix scraping bug when operator specifies custom node HTTP API port in bond 2025-10-31 09:02:13 +00:00
import this 168baa5071 [Feature/operators]: QUIC bridge deployment script v2 (#6145)
* new quick deployment script

* docs tweak

* update script to use .deb postinst

* final clean - ready to go

* correct nym-node config dir search with a fallback
2025-10-31 09:02:13 +00:00
Simon Wicky 7aef468839 remove unused deps (#6151) 2025-10-31 09:02:12 +00:00
Simon Wicky cb3ccd7f7e use typed builder (#6150) 2025-10-31 09:02:12 +00:00
Simon Wicky 16509dbace allow overwriting existing sdk shutdown manager 2025-10-31 09:02:12 +00:00
Simon Wicky b907ccbd5b typo 2025-10-31 09:02:12 +00:00
Simon Wicky 7b3194d7d2 calling for shutdown from the MixTrafficController 2025-10-31 09:02:12 +00:00
Jędrzej Stuczyński 0b81edfc66 using same hierarchy of trackers for client shutdown control 2025-10-31 09:02:12 +00:00
Tommy Verrall 8c6150e5ef Internal comments 2025-10-31 09:02:12 +00:00
Tommy Verrall 46164a389a Fix comments 2025-10-31 09:02:12 +00:00
Tommy Verrall 96d256b48f Better message to come in the PR description 2025-10-31 09:02:12 +00:00
Simon Wicky f98b93f60e tommy is too quick 2025-10-31 09:02:12 +00:00
Simon Wicky 0f6c356f39 configurable mixnet client startup timeout 2025-10-31 09:02:12 +00:00
p17o cf3f5d9a53 Update quic_bridge_deployment.sh for IPv4 and .deb package (#6138)
Updated ping commands to explicitly use IPv4 and adjusted file permission checks with sudo. Changed the forward address prompt to specify IPv4 and modified the binary download process to fetch and install the latest .deb release URL automatically.
2025-10-31 09:02:12 +00:00
Jędrzej Stuczyński 2f47e6349a bugfix: update internal owner address in transferred share (#6139) 2025-10-31 09:02:12 +00:00
Tommy Verrall 4b2b5390d3 Last failing test - fix 2025-10-31 09:02:12 +00:00
Tommy Verrall 734ff63f6d Use explicit Vec<ApiUrl> handling in BaseClientBuilder
- Replace NymNetworkDetails with explicit API URL handling
- Fix deprecated from_network() usage and improve fallback logic
- Add URL validation and remove unused backwards compatibility
2025-10-31 09:02:11 +00:00
Tommy Verrall 910c24e7da Actually commit the recommended changes 2025-10-31 09:02:11 +00:00
Tommy Verrall c96b73172a Fix broken tests in CI 2025-10-31 09:02:11 +00:00
Tommy Verrall 823c2c7262 Replace deprecated from_network() with new_with_fronted_urls() 2025-10-31 09:02:11 +00:00
Jędrzej Stuczyński 03d1b72a9e feat: expose more explicit new_with_fronted_urls builder for http API client (#6136) 2025-10-31 09:02:10 +00:00
Jędrzej Stuczyński 066669440f bugfix: update stored epoch share when changing ownership (#6135) 2025-10-31 09:01:27 +00:00
Jędrzej Stuczyński 9fb3443fd1 bugfix: update stored epoch share when changing announce address (#6131)
* bugfix: update stored epoch share when changing announce address

* chore: remove placeholder legacy mixnode bonding test [mixnet contract]
2025-10-31 09:01:27 +00:00
Tommy Verrall cd89a590b5 Fix new_from_env() to populate nym_api_urls for domain fronting 2025-10-31 09:01:27 +00:00
Tommy Verrall 06cb8bd969 fix all clippy messages 2025-10-31 09:01:27 +00:00
Tommy Verrall c72fef169c Add more tests for retry logic 2025-10-31 09:01:27 +00:00
Tommy Verrall 3c27eb41b8 Fix confusing tracing logs 2025-10-31 09:01:27 +00:00
Tommy Verrall 2428374fe7 Fix retries - this is working 2025-10-31 09:01:27 +00:00
Tommy Verrall 5a491f0a7e Add configuration-based domain fronting support
Changes:
- Add network_details field to BaseClientBuilder (optional, backwards compatible)
- Add with_network_details() method for opt-in domain fronting
- Update construct_nym_api_client() to use from_network() when network_details provided
- Enable network-defaults feature in nym-client-core Cargo.toml
- SDK passes network_details to BaseClientBuilder
2025-10-31 09:01:27 +00:00
Tommy Verrall 530f9ccb6f Fix CI issues 2025-10-31 09:01:26 +00:00
Tommy Verrall 2228a81d43 Allow clippy::enum_variant_names for BuilderConfigError 2025-10-31 09:01:26 +00:00
Tommy Verrall c8a1b53071 Improve error handling
Changes:
- Replace String error with BuilderConfigError enum in BuilderConfigBuilder
- Update tests to use pattern matching instead of string assertions
2025-10-31 09:01:26 +00:00
Tommy Verrall b896aaaed1 - Add DEFAULT_NYM_API_RETRIES constant (replaces magic number 3)
- Run cargo fmt on all affected packages
- All clippy warnings resolved
2025-10-31 09:01:26 +00:00
Tommy Verrall 828ffc6710 not sure what happened but it's fixed 2025-10-31 09:01:26 +00:00
Andy Duplain 05cdb27029 VPN-4262: Update Url to return url and front fields.
The VPN client is using the `Url` type alot now and in order to avoid
double URL-parsing we would like the content of the `Url` type exposed.
2025-10-31 09:01:26 +00:00
Tommy Verrall 3f29d3eba5 Add accessor methods for Url internals
Add inner_url() and fronts() accessor methods to nym_http_api_client::Url
for VPN client integration
2025-10-31 09:01:22 +00:00
Tommy Verrall aa2c41dc41 Merge resolution 2025-10-31 09:01:02 +00:00
Tommy Verrall 048de771ab Remove tests for removed with_nym_api_client method
These tests were referencing with_nym_api_client() which was removed when
cleaning domain fronting code from this branch
2025-10-31 09:01:02 +00:00
Tommy Verrall e1b06f02f3 Add optional builder pattern for BuilderConfig (non-breaking)
Addresses @jstuczyn's feedback about too many arguments by adding
BuilderConfigBuilder as an alternative to the existing new() method.
2025-10-31 09:01:02 +00:00
Tommy Verrall b5b8b8f224 fix conversion type && make the retry count configurable 2025-10-31 09:01:02 +00:00
Tommy Verrall 2d7141dfb1 Revert node filtering changes per Andrew's feedback
Andrew clarified that get_basic_entry_assigned_nodes_v2() already filters by
supported_roles.entry
2025-10-31 09:01:02 +00:00
Tommy Verrall a07522258f Remove domain fronting code to keep gateway changes only
This branch now contains only gateway registration improvements:
- Multiple URL fallback support in gateways_for_init()
- Get all entry-capable nodes for registration
- Performance and code quality improvements
2025-10-31 09:00:17 +00:00
Tommy Verrall 547a441002 Address PR feedback: simplify code and reduce log noise
- Reverted all changes to topology_control/nym_api_provider.rs
- Changed info/warn logs to debug for custom client messages
- Removed unused _rng parameter from gateways_for_init()
- Simplified URL builder to always use new_with_urls()
2025-10-31 09:00:17 +00:00
Tommy Verrall 93208fb5e0 Fix clippy warnings: use arrays instead of vec! in tests 2025-10-31 09:00:17 +00:00
Bogdan-Ștefan Neacşu c9b50dd979 Introduce event backchannel (#6119)
* Introduce even backchannel

* Rust fmt

* Rename Event to MixnetClientEvent

* Use unbounded_send for events

* Remove unused file

* Remove mut borrow

* Event hierarchy and mixnet client intermediary

* Export MixTrafficEvent in sdk
2025-10-31 09:00:15 +00:00
Jędrzej Stuczyński 74cdfd5d94 Merge pull request #6099 from nymtech/bugfix/incompatibility-fixes
Bugfix/incompatibility fixes
2025-10-31 08:59:40 +00:00
Jędrzej Stuczyński 953e813f0e Bugfix/bloomfilters purge (#6089)
* remove all old bloomfilters upon starting binary

* remove old bloomfilter file upon purging secondary data
2025-10-31 08:59:38 +00:00
Tommy Verrall 29cf5058a6 feat: pass custom HTTP client through SDK stack for domain fronting
- Add with_nym_api_client() to BaseClientBuilder, MixnetClientBuilder, and RegistrationClientBuilderConfig

- Modify nym_api_provider to fetch all nodes then filter by supported_roles.entry (fixes metadata inconsistency)

- Update helpers.rs to build HTTP client with all nym_apis URLs and retries for fallback support

- Fix SDK to use entry_capable_nodes() instead of entry_gateways() for broader gateway selection

This enables domain fronting and URL rotation throughout the entire SDK stack, improving censorship resistance and connection reliability. All changes are backward compatible - custom client is optional.
2025-10-31 08:57:08 +00:00
Tommy Verrall a2856552d8 enable URL rotation and retries for mixnet gateway init 2025-10-31 08:57:08 +00:00
Andrej Mihajlov a33c603471 Update dirs to 6.0 2025-10-31 08:57:08 +00:00
Jędrzej Stuczyński a9f9266992 bugfix: nym-credential-proxy query params parsing regression (#6121) 2025-10-31 08:57:08 +00:00
Tommy Verrall cf34d0d24a Skip ipv6 metadata endpoint request (#6118)
Co-authored-by: Tommy Verrall <tommy@nymtech.net>
2025-10-31 08:57:07 +00:00
Jędrzej Stuczyński 5fa7b0a709 bugfix: revert some dep updates introduced in #6043 (#6120) 2025-10-31 08:57:07 +00:00
Andrej Mihajlov e232b4fd24 Revert "Propagate cancel token to mixnet client"
This reverts commit 50a259d454.
2025-10-31 08:57:07 +00:00
Jędrzej Stuczyński 609f174e8d chore: restore pending dkg contract state migration (#6116)
since it has not yet been run on mainnet
2025-10-31 08:57:07 +00:00
benedetta davico a0f4627647 Update lib.go 2025-10-31 08:57:07 +00:00
Jędrzej Stuczyński 258f8f5f5d bugfix: retrieve and update ticketbook in the same query (#6101)
* bugfix: retrieve and update ticketbook in the same query

* bump up NS version

* Update Cargo.toml

* remove SKIP LOCKED part of the query

---------

Co-authored-by: benedetta davico <46782255+benedettadavico@users.noreply.github.com>
2025-10-31 08:57:07 +00:00
mfahampshire 38220e05f1 DOCS Jarlsberg Release (#6111)
* First pass release notes

* build info
2025-10-31 08:57:07 +00:00
Andrej Mihajlov 6250ebe235 Propagate cancel token to mixnet client 2025-10-31 08:57:07 +00:00
mfahampshire a55323c0e2 Patch for operators to open wg metadata port (#6106) 2025-10-31 08:57:07 +00:00
Jędrzej Stuczyński baa8ac3610 bugfix: use custom topology provider for list of init gateways (#6092) 2025-10-31 08:57:07 +00:00
Jędrzej Stuczyński 933da11e8f bugfix: include network name in the default gateway probe config path (#6100) 2025-10-31 08:57:07 +00:00
Jędrzej Stuczyński 0469036da4 feat: expose obtaining reference to Mnemonic from DirectSecp256k1HdWallet (#6083)
* feat: expose obtaining reference to Mnemonic from DirectSecp256k1HdWallet

* updated getters for stringified mnemonic
2025-10-31 08:57:07 +00:00
Georgio Nicolas 63f9a856fa Another offering for Clippy 2025-10-31 08:57:07 +00:00
Georgio Nicolas c068948c62 Offerings for clippy 2025-10-31 08:57:06 +00:00
Georgio Nicolas 0105f9fa5e Precompute BSGS table 2025-10-31 08:57:06 +00:00
Georgio Nicolas 004c737965 Use LazyLock to precompute generators 2025-10-31 08:57:06 +00:00
Georgio Nicolas 549121ca32 Fix clippy suggestion 2025-10-31 08:57:06 +00:00
Georgio Nicolas 3f2278dafc Fix zeroization 2025-10-31 08:57:06 +00:00
Georgio Nicolas 25ce0ac814 replace unsafe static values by function calls 2025-10-31 08:57:06 +00:00
Mark Sinclair 38d313a101 ns-api: add descriptions to dVPN gateway responses (#6102)
* ns-api: add descriptions to dVPN gateway responses

* clippy

* fmt

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-10-31 08:57:06 +00:00
import this f67bc0ead5 [DOCs/operators] QUIC deployment script & docs (#6098)
* add quic_bridge_deployment.sh

* create a snippet with quick install steps

* add quic deployment to changelog

* add quic deployment to node config page

* add version compatibility callout

* last edits and scraped stats update

* correct name of QUIC snippet

* fix naming

* fix naming

* re-run python-prebuild.sh aka time-now updated

* attempt to fix vercel build the hard way

* rerun npm

* build with pnpm

* restore lock file and rebuild w pnpm

* chore: update pnpm lockfile

* attempt to fix build

* attempt to fix runtime builds

* update ci-docs run OS
2025-10-31 08:57:06 +00:00
Mark Sinclair 90aaa3572d Update ci-docs.yml 2025-10-31 08:57:06 +00:00
Mark Sinclair ecc61e4a4a NS API: use new probe download filesize and milliseconds field (#6097)
* use milliseconds field

* change score thresholds

* bump to version 4.0.8

* NS API: adjust score categories (#6103)

* testing scores

* test version

* Update Cargo.toml

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
Co-authored-by: benedetta davico <46782255+benedettadavico@users.noreply.github.com>
2025-10-31 08:57:06 +00:00
Jędrzej Stuczyński 22ac4919e5 bugfix: testnet manager 02sql migration (#6096) 2025-10-31 08:57:06 +00:00
Jędrzej Stuczyński a1e7cc8e87 chore: remove unnecessary closure in 'calculate_score' inside node-status-api 2025-10-31 08:57:06 +00:00
Mark Sinclair 57df00637c ns-api: use download files size from probes instead of parsing filenames 2025-10-31 08:57:06 +00:00
Jędrzej Stuczyński c7eb3bdb7b moved nym-gateway-probe to monorepo and updated rust-edition to 2024 (#6094)
dont build netstack in CI

additional rust 2024 fixes

fixes

removed temp.rs

first round of cleanup

removed duplicated NS types

moved gateway probe to the monorepo
2025-10-31 08:57:05 +00:00
Mark Sinclair 8f9b704541 ns-api: add new fields for probe output for query_metadata and download file size and duration in ms (#6091)
Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-10-31 08:56:49 +00:00
Mark Sinclair 6a956c790a NS API: clamp load to offline when score is offline and add mixnet_score field to preformance_v2 (#6076)
* ns-api: when `score` is `Offline`, clamp `load` to `Offline`

* ns-api: bump version

* ns-api: add mixnet score field to performance_v2 struct

---------

Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com>
2025-10-31 08:56:49 +00:00
mfahampshire 2ff5c7221a Max/fix wasm client + build commands (#6043)
* Debug logging 

* Yield based logging

* Reintroduce non-dummy task manager, try add counting for
BatchMessageSender, a couple of compiler target introductions on use
statements.

* Fixed time runtime err

* Uncomment forgetme/rememberme

* remove diffs from debug

* missed commented out forgetme

* yet more forgetme comments

* * Added missing clientreqestsender clone to wasm client to stop
  premature drop & busyloop
* Removed hacky mem::forget fix

* Remove debug panic_hook

* Conditional import + use of wasm_utils::console_log

* add wasm_util dep

* Commenting out or removing debug logging

* Remove missed comment

* cleanup gitignore

* clippy

* update go version in ci

* removed unused deps

* add clippy ignore

* remove mixfetch from ci build

* add minifetch fix

* comment out unused ts builds

* stop contract clients killing ci for the moment

* wasm target locking for imports

* Either remove console_log! macro or introduce cfg(debug_assertions)

* downgrade netlink

* debug assertions for console_log import

* modify config logging (debug -> normal)

* remove clone for client_request_sender + grab directly in struct
  creation

* reintroduce debug print for config in debug mode

* remove ood / unused custom topology from worker example file

* clippy

* clippy - ignore todo() tests

* modified humantime test in line with new parsing rules
2025-10-31 08:56:49 +00:00
benedetta davico 2235a6e147 Merge pull request #6113 from nymtech/release/2025.18-jarlsberg
Merge release/2025.18-jarlsberg to master
2025-10-15 10:22:16 +02:00
benedettadavico db6defa122 update changelog 2025-10-14 12:07:26 +02:00
Jędrzej Stuczyński df7768dec0 Bugfix/bloomfilters purge (#6089)
* remove all old bloomfilters upon starting binary

* remove old bloomfilter file upon purging secondary data
2025-10-06 14:02:32 +01:00
benedettadavico f3a449b7cc bump versions 2025-10-06 14:38:00 +02:00
benedetta davico cf21593ffa Merge pull request #6080 from nymtech/release/2025.17-isabirra
Merge release/2025.17-isabirra to master
2025-10-02 16:06:41 +02:00
benedetta davico f0d8dabb9f Merge pull request #6042 from nymtech/release/2025.16-halloumi
Merge release/2025.16-halloumi to master
2025-09-17 14:20:19 +02:00
benedetta davico f105bcbafe Merge pull request #5968 from nymtech/release/2025.15-gruyere
merge gruyere to master
2025-08-21 12:20:35 +02:00
benedetta davico dc0f4af2c1 Merge pull request #5937 from nymtech/release/2025.14-feta 2025-08-13 11:12:19 +02:00
benedetta davico 2a621e07a8 Merge pull request #5907 from nymtech/release/2025.13-emmental
Merge release/2025.13-emmental to master
2025-07-22 16:23:44 +02:00
benedetta davico 485aeebabd Merge pull request #5886 from nymtech/release/2025.12-dolcelatte
Merge release/2025.12-dolcelatte to master
2025-07-09 15:25:16 +02:00
benedetta davico 3b726bada9 Merge pull request #5839 from nymtech/release/2025.11-cheddar
merge release/2025.11-cheddar to master
2025-06-11 13:09:43 +02:00
benedetta davico 1d1b2e17d2 Merge pull request #5807 from nymtech/release/2025.10-brie 2025-05-28 09:38:15 +02:00
benedetta davico b5b2dbdfd8 Merge pull request #5776 from nymtech/release/2025.9-appenzeller
Release/2025.9-appenzeller to master
2025-05-16 13:23:10 +02:00
benedetta davico 82806f47d8 Merge pull request #5735 from nymtech/release/2025.8-tourist
Merge release/2025.8-tourist to master
2025-05-05 12:11:39 +02:00
benedetta davico c6f85cf23e Merge pull request #5727 from nymtech/release/2025.7-tex
Merge tex to master
2025-04-22 10:50:43 +02:00
benedetta davico ed8de7234d Merge pull request #5672 from nymtech/release/2025.6-chuckles
Merge release/2025.6-chuckles into master
2025-04-02 10:34:51 +02:00
benedetta davico e25d83b047 Merge pull request #5641 from nymtech/release/2025.5-chokito
Merge chokito to master
2025-03-24 10:14:50 +01:00
Jędrzej Stuczyński 9974d480b5 Merge pull request #5574 from nymtech/release/2025.4-dorina-patched
Release/2025.4-dorina-patched to master
2025-03-11 10:37:06 +00:00
benedetta davico 2211f13cdd Merge pull request #5551 from nymtech/release/2025.4-dorina
Merge release/2025.4-dorina to master
2025-03-04 13:55:27 +01:00
benedetta davico 4505f18a02 Merge pull request #5485 from nymtech/release/2025.3-ruta
Release/2025.3 ruta to master
2025-02-18 10:08:08 +01:00
benedetta davico a717a18948 Merge pull request #5430 from nymtech/release/2025.2-hu
Merge release/2025.2-hu to master
2025-02-06 13:58:55 +01:00
25 changed files with 390 additions and 194 deletions
Generated
+3 -3
View File
@@ -133,9 +133,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "ammonia"
version = "4.1.1"
version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6b346764dd0814805de8abf899fe03065bcee69bb1a4771c785817e39f3978f"
checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6"
dependencies = [
"cssparser",
"html5ever",
@@ -6546,7 +6546,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "4.0.10"
version = "4.0.11-rc1"
dependencies = [
"ammonia",
"anyhow",
@@ -1 +1 @@
Tuesday, October 14th 2025, 11:34:14 UTC
Thursday, October 30th 2025, 13:00:59 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]
-o [OUTPUT], --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) | US, DE, UK, ESP, FR | nan | No | nan | 07/2025 |
| [Ionos](https://www.ionos.com/servers/amd-servers) | USA, DE, UK, ESP, FR | nan | No | nan | 07/2025 |
| [IsHosting](https://ishosting.com/en) | Brazil, Netherlands | Yes, based on ticket | Yes | Expensive | 05/2024 |
| [Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134) | US, NL, DE, UK, CA, SG, JP, AUS, HK | nan | No | KYC mandatory | 07/2025 |
| [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 |
| [Linode](https://linode.com) | USA, Canada, Japan, India, Indonesia, Sweden, Netherlands, Germany, Brazil, France, UK, Australia, Italy | Yes out of the box | No, only through [BitLAunch](https://bitlaunch.io) | IPv6 sometimes need to be re-added in Networking tab, no reboot needed | 05/2024 |
| [LiteServer](https://liteserver.nl) | Netherlands | Yes, on by default | Yes | Very reliable Dutch provider. They do allow Relay nodes but for Exit nodes you need to contact them. Always check T&C https://liteserver.nl/legal | 07/2024 |
| [Lowendbox](https://lowendbox.com/category/dedicated-servers) | | | | Just an aggregator with good offers | 07/2025 |
| [M247](https://m247.com/eu/services/host/dedicated-servers/) | UK, Austria, Br, Sw, Jp, Poland, Fr, USA, Netherlands | Yes | No | nan | 07/2025 |
| [Mebilcom](https://www.melbicom.net/dedicatedserver/) | NL, US, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL | nan | No | nan | 07/2025 |
| [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 |
| [Mevspace](https://mevspace.com) | Poland | Yes, on by default | Yes | Flexible Polish providers with 3 DCs in Poland. They do allow Tor Exit nodes but you may need a dedicated server for this. Make sure you open a ticket to check. As of today's date, they have 48h for 1 EUR tariff | 07/2024 |
| [Misaka](https://www.misaka.io/) | South Africa | Yes, native support | No | Very Expensive | 05/2024 |
| [NiceVPS](https://nicevps.net/) | Netherlands | Yes | nan | nan | 07/2025 |
| [Njalla](https://nja.la) | Sweden | Yes | Yes | Privacy vandguards! The biggest VPS 45 is 3 cores only, but it works better than many “larger” servers on the market. | 05/2024 |
| [OVH](https://us.ovhcloud.com/bare-metal/rise/rise-3/) | USA, DE, FR, UK, PL, CA | | No | Not all locations always available | 07/2025 |
| [Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6) | PL, FR, NL, UA, US, BG, RO, DK, ESP, NO, CZ, RS, IE, IT, UK, HU, CH, SK, AT, BE, BA, HK, JP, SG, LU, AU, SWE, UAE, BR, CR, MX, GR, CL, MA, AR | Yes | No | nan | 07/2025 |
| [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 |
| [PrivateLayer](https://privatelayer.com) | Swiss | Yes | Yes | Slow customer response | 07/2025 |
| [Privex](https://www.privex.io/tor-exit-policy/) | USA, Germany, Sweden | Yes | Yes | nan | 07/2025 |
| [Psychz](https://www.psychz.net) | US, UK, Brazil, Japan, Russia, South Africa and many more | Yes | nan | nan | 07/2025 |
| [Psychz](https://www.psychz.net) | USA, 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 |
+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, US, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL",,No,,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
[Servermania](https://www.servermania.com/dedicated-servers-hosting.htm),"USA, Canada",,No,,07/2025
[Oneprovider](https://oneprovider.com/en/dedicated-servers/ipv6),"PL, FR, NL, UA, US, BG, RO, DK, ESP, NO, CZ, RS, IE, IT, UK, HU, CH, SK, AT, BE, BA, HK, JP, SG, LU, AU, SWE, UAE, BR, CR, MX, GR, CL, MA, AR",Yes,No,,07/2025
[Ionos](https://www.ionos.com/servers/amd-servers),"US, DE, UK, ESP, FR",,No,,07/2025
[Leaseweb](https://www.leaseweb.com/en/configure/vc/product/entityKey/DEDSER02_NEW_ORDER_BUSINESS_R740XD-24SFF-6134),"US, NL, DE, UK, CA, SG, JP, AUS, HK",,No,KYC mandatory,07/2025
[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
[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),"US, UK, Brazil, Japan, Russia, South Africa and many more",Yes,,,07/2025
[Psychz](https://www.psychz.net),"USA, 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, US, DE, UAE, NG, ESP, IN, IT, FR, LT, SG, BG, LV, PL NL, USA, 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, 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 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
27 [Ionos](https://www.ionos.com/servers/amd-servers) US, DE, UK, ESP, FR USA, 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) US, NL, DE, UK, CA, SG, JP, AUS, HK USA, 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) US, UK, Brazil, Japan, Russia, South Africa and many more USA, 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
@@ -49,6 +49,90 @@ 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)
@@ -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-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
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
```
@@ -50,7 +50,7 @@ impl AuthClientMixnetListener {
}
}
async fn run(mut self) {
async fn run(mut self) -> Self {
let mixnet_cancel_token = self.mixnet_client.cancellation_token();
self.shutdown_token.run_until_cancelled(async {
loop {
@@ -95,8 +95,12 @@ impl AuthClientMixnetListener {
tracing::debug!("AuthClientMixnetListener is shutting down");
}).await;
tracing::debug!("AuthClientMixnetListener: Disconnect mixnet client");
if !mixnet_cancel_token.is_cancelled() {
self
}
// Disconnects the mixnet client and effectively drop itself, since it doesn't work without one, and reconnecting isn't supported
pub async fn disconnect_mixnet_client(self) {
if !self.mixnet_client.cancellation_token().is_cancelled() {
self.mixnet_client.disconnect().await;
}
}
@@ -124,7 +128,7 @@ pub struct AuthClientMixnetListenerHandle {
message_sender: MixnetMessageInputSender,
cancellation_token: CancellationToken,
mixnet_cancellation_token: CancellationToken,
handle: JoinHandle<()>,
handle: JoinHandle<AuthClientMixnetListener>,
}
impl AuthClientMixnetListenerHandle {
@@ -144,8 +148,13 @@ impl AuthClientMixnetListenerHandle {
// If shutdown was externally called, that call is a no-op
// If we're only stopping this, it is very much needed
self.cancellation_token.cancel();
if let Err(e) = self.handle.await {
tracing::error!("Error waiting for auth clients mixnet listener to stop: {e}")
match self.handle.await {
Ok(auth_client_mixnet_listener) => {
auth_client_mixnet_listener.disconnect_mixnet_client().await;
}
Err(e) => {
tracing::error!("Error waiting for auth clients mixnet listener to stop: {e}");
}
}
}
}
+1 -1
View File
@@ -765,7 +765,7 @@ async fn connect_exit(
);
// The IPR supports cancellation, but it's unused in the gateway probe
let cancel_token = CancellationToken::new();
let mut ipr_client = IprClientConnect::new(mixnet_client, cancel_token);
let mut ipr_client = IprClientConnect::new(mixnet_client, cancel_token).await;
let maybe_ip_pair = ipr_client.connect(exit_router_address).await;
let mixnet_client = ipr_client.into_mixnet_client();
+1 -1
View File
@@ -43,7 +43,7 @@ pub struct IprClientConnect {
}
impl IprClientConnect {
pub fn new(mixnet_client: MixnetClient, cancel_token: CancellationToken) -> Self {
pub async fn new(mixnet_client: MixnetClient, cancel_token: CancellationToken) -> Self {
Self {
mixnet_client,
connected: ConnectionState::Disconnected,
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n node_id,\n ed25519_identity_pubkey,\n total_stake,\n ip_addresses as \"ip_addresses!: serde_json::Value\",\n mix_port,\n x25519_sphinx_pubkey,\n node_role as \"node_role: serde_json::Value\",\n supported_roles as \"supported_roles: serde_json::Value\",\n entry as \"entry: serde_json::Value\",\n performance,\n self_described as \"self_described: serde_json::Value\",\n bond_info as \"bond_info: serde_json::Value\"\n FROM\n nym_nodes\n ORDER BY\n node_id\n ",
"query": "SELECT\n node_id,\n ed25519_identity_pubkey,\n total_stake,\n ip_addresses as \"ip_addresses!: serde_json::Value\",\n mix_port,\n x25519_sphinx_pubkey,\n node_role as \"node_role: serde_json::Value\",\n supported_roles as \"supported_roles: serde_json::Value\",\n entry as \"entry: serde_json::Value\",\n performance,\n self_described as \"self_described: serde_json::Value\",\n bond_info as \"bond_info: serde_json::Value\",\n http_api_port\n FROM\n nym_nodes\n WHERE\n self_described IS NOT NULL\n AND\n bond_info IS NOT NULL\n ",
"describe": {
"columns": [
{
@@ -62,6 +62,11 @@
"ordinal": 11,
"name": "bond_info: serde_json::Value",
"type_info": "Jsonb"
},
{
"ordinal": 12,
"name": "http_api_port",
"type_info": "Int4"
}
],
"parameters": {
@@ -79,8 +84,9 @@
true,
false,
true,
true,
true
]
},
"hash": "c48d04fc3de59dd484f0a63d40336ced54e08785f77e9ef85f3157d004ec85dc"
"hash": "0b51df277ed66c6553f66af9b135342dee177abc1c92e4a89147de3c22d3d1a5"
}
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n node_id,\n ed25519_identity_pubkey,\n total_stake,\n ip_addresses as \"ip_addresses!: serde_json::Value\",\n mix_port,\n x25519_sphinx_pubkey,\n node_role as \"node_role: serde_json::Value\",\n supported_roles as \"supported_roles: serde_json::Value\",\n entry as \"entry: serde_json::Value\",\n performance,\n self_described as \"self_described: serde_json::Value\",\n bond_info as \"bond_info: serde_json::Value\"\n FROM\n nym_nodes\n WHERE\n self_described IS NOT NULL\n AND\n bond_info IS NOT NULL\n ",
"query": "SELECT\n node_id,\n ed25519_identity_pubkey,\n total_stake,\n ip_addresses as \"ip_addresses!: serde_json::Value\",\n mix_port,\n x25519_sphinx_pubkey,\n node_role as \"node_role: serde_json::Value\",\n supported_roles as \"supported_roles: serde_json::Value\",\n entry as \"entry: serde_json::Value\",\n performance,\n self_described as \"self_described: serde_json::Value\",\n bond_info as \"bond_info: serde_json::Value\",\n http_api_port\n FROM\n nym_nodes\n ORDER BY\n node_id\n ",
"describe": {
"columns": [
{
@@ -62,6 +62,11 @@
"ordinal": 11,
"name": "bond_info: serde_json::Value",
"type_info": "Jsonb"
},
{
"ordinal": 12,
"name": "http_api_port",
"type_info": "Int4"
}
],
"parameters": {
@@ -79,8 +84,9 @@
true,
false,
true,
true,
true
]
},
"hash": "283f49a65c7d70bf271702ff6a5c7ad6e68c81932d295ff18ed198c54706a57c"
"hash": "3ddc12cc4e1796b787a50c40560d2bd71d1cfe5f5265e6f161b3122d1317a421"
}
@@ -1,26 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO nym_nodes\n (node_id, ed25519_identity_pubkey,\n total_stake,\n ip_addresses, mix_port,\n x25519_sphinx_pubkey, node_role,\n supported_roles, entry,\n self_described,\n bond_info,\n performance, last_updated_utc\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)\n ON CONFLICT(node_id) DO UPDATE SET\n ed25519_identity_pubkey=excluded.ed25519_identity_pubkey,\n ip_addresses=excluded.ip_addresses,\n mix_port=excluded.mix_port,\n x25519_sphinx_pubkey=excluded.x25519_sphinx_pubkey,\n node_role=excluded.node_role,\n supported_roles=excluded.supported_roles,\n entry=excluded.entry,\n self_described=excluded.self_described,\n bond_info=excluded.bond_info,\n performance=excluded.performance,\n last_updated_utc=excluded.last_updated_utc\n ;",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Varchar",
"Int8",
"Jsonb",
"Int4",
"Varchar",
"Jsonb",
"Jsonb",
"Jsonb",
"Jsonb",
"Jsonb",
"Varchar",
"Int4"
]
},
"nullable": []
},
"hash": "b010fb91828f7e4f0b72bdfe3b58b2abb437cccdb6ebd2e1087cc822ed737b0e"
}
@@ -0,0 +1,27 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO nym_nodes\n (node_id, ed25519_identity_pubkey,\n total_stake,\n ip_addresses, mix_port,\n x25519_sphinx_pubkey, node_role,\n supported_roles, entry,\n self_described,\n bond_info,\n performance, last_updated_utc, http_api_port\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)\n ON CONFLICT(node_id) DO UPDATE SET\n ed25519_identity_pubkey=excluded.ed25519_identity_pubkey,\n ip_addresses=excluded.ip_addresses,\n mix_port=excluded.mix_port,\n x25519_sphinx_pubkey=excluded.x25519_sphinx_pubkey,\n node_role=excluded.node_role,\n supported_roles=excluded.supported_roles,\n entry=excluded.entry,\n self_described=excluded.self_described,\n bond_info=excluded.bond_info,\n performance=excluded.performance,\n last_updated_utc=excluded.last_updated_utc,\n http_api_port=excluded.http_api_port\n ;",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Varchar",
"Int8",
"Jsonb",
"Int4",
"Varchar",
"Jsonb",
"Jsonb",
"Jsonb",
"Jsonb",
"Jsonb",
"Varchar",
"Int4",
"Int4"
]
},
"nullable": []
},
"hash": "dde9aff827c34086077927bbe33fa3d5c939e7122ba7c88b78a353f00b271ec2"
}
@@ -3,7 +3,7 @@
[package]
name = "nym-node-status-api"
version = "4.0.10"
version = "4.0.11-rc1"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -0,0 +1,2 @@
ALTER TABLE nym_nodes
ADD COLUMN IF NOT EXISTS http_api_port INTEGER;
@@ -1,5 +1,5 @@
use crate::ticketbook_manager::TicketbookManagerConfig;
use clap::Parser;
use clap::{Parser, Subcommand};
use nym_bin_common::bin_info;
use nym_credential_proxy_lib::shared_state::ecash_state::TicketType;
use reqwest::Url;
@@ -105,6 +105,19 @@ pub(crate) struct Cli {
#[clap(flatten)]
pub(crate) ticketbook: TicketbookArgs,
#[command(subcommand)]
pub(crate) command: Option<Commands>,
}
#[derive(Subcommand, Debug)]
pub(crate) enum Commands {
/// Scrape a single node and output detailed debug logs
ScrapeNode {
/// The id of the node to scrape
#[arg(long)]
node_id: i64,
},
}
#[derive(Debug, Parser)]
@@ -381,7 +381,7 @@ impl ScrapeNodeKind {
pub(crate) struct ScraperNodeInfo {
pub node_kind: ScrapeNodeKind,
pub hosts: Vec<String>,
pub http_api_port: i64,
pub http_api_port: Option<u16>,
}
impl ScraperNodeInfo {
@@ -395,8 +395,21 @@ impl ScraperNodeInfo {
format!("http://{}", host),
]);
if self.http_api_port != DEFAULT_NYM_NODE_HTTP_PORT as i64 {
urls.insert(0, format!("http://{}:{}", host, self.http_api_port));
if let Some(custom_http_api_port) = self.http_api_port {
urls = Vec::new();
for host in &self.hosts {
urls.append(&mut vec![format!(
"http://{}:{}",
host, custom_http_api_port
)]);
}
// do not fall back to default ports, if the operator sets a custom http api port
// in their bond, use it and error out if it's not available
// this will correctly handle cases where some operators run multiple nodes
// on a single IP address and assign different custom http port apis at bond time
// urls.insert(0, format!("http://{}:{}", host, custom_http_api_port));
}
}
@@ -423,6 +436,7 @@ pub(crate) struct NymNodeDto {
pub performance: String,
pub self_described: Option<serde_json::Value>,
pub bond_info: Option<serde_json::Value>,
pub http_api_port: Option<i32>,
}
#[allow(dead_code)] // it's not dead code but clippy doesn't detect usage in sqlx macros
@@ -440,6 +454,7 @@ pub(crate) struct NymNodeInsertRecord {
pub entry: Option<serde_json::Value>,
pub self_described: Option<serde_json::Value>,
pub bond_info: Option<serde_json::Value>,
pub http_api_port: Option<i32>,
pub last_updated_utc: i64,
}
@@ -456,6 +471,12 @@ impl NymNodeInsertRecord {
.map(|info| decimal_to_i64(info.total_stake()))
.unwrap_or(0);
let entry = serialize_opt_to_value!(skimmed_node.entry)?;
let http_api_port = bond_info.and_then(|bond| {
bond.bond_information
.node
.custom_http_port
.map(|port| port as i32)
});
let bond_info = serialize_opt_to_value!(bond_info)?;
let self_described = serialize_opt_to_value!(self_described)?;
@@ -472,6 +493,7 @@ impl NymNodeInsertRecord {
entry,
self_described,
bond_info,
http_api_port,
last_updated_utc: now,
};
@@ -35,7 +35,8 @@ pub(crate) async fn get_all_nym_nodes(pool: &DbPool) -> anyhow::Result<Vec<NymNo
entry as "entry: serde_json::Value",
performance,
self_described as "self_described: serde_json::Value",
bond_info as "bond_info: serde_json::Value"
bond_info as "bond_info: serde_json::Value",
http_api_port
FROM
nym_nodes
ORDER BY
@@ -72,7 +73,8 @@ pub(crate) async fn get_described_bonded_nym_nodes(
entry as "entry: serde_json::Value",
performance,
self_described as "self_described: serde_json::Value",
bond_info as "bond_info: serde_json::Value"
bond_info as "bond_info: serde_json::Value",
http_api_port
FROM
nym_nodes
WHERE
@@ -115,9 +117,9 @@ pub(crate) async fn update_nym_nodes(
supported_roles, entry,
self_described,
bond_info,
performance, last_updated_utc
performance, last_updated_utc, http_api_port
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
ON CONFLICT(node_id) DO UPDATE SET
ed25519_identity_pubkey=excluded.ed25519_identity_pubkey,
ip_addresses=excluded.ip_addresses,
@@ -129,7 +131,8 @@ pub(crate) async fn update_nym_nodes(
self_described=excluded.self_described,
bond_info=excluded.bond_info,
performance=excluded.performance,
last_updated_utc=excluded.last_updated_utc
last_updated_utc=excluded.last_updated_utc,
http_api_port=excluded.http_api_port
;",
record.node_id,
record.ed25519_identity_pubkey,
@@ -144,6 +147,7 @@ pub(crate) async fn update_nym_nodes(
record.bond_info,
record.performance,
record.last_updated_utc as i32,
record.http_api_port,
)
.execute(&mut *tx)
.await
@@ -21,10 +21,11 @@ pub(crate) async fn get_nodes_for_scraping(pool: &DbPool) -> Result<Vec<ScraperN
let skimmed_nodes = queries::get_described_bonded_nym_nodes(pool)
.await
.map(|nodes_dto| {
nodes_dto.into_iter().filter_map(|node| {
let node_id = node.node_id;
match SkimmedNode::try_from(node) {
Ok(node) => Some(node),
nodes_dto.into_iter().filter_map(|node_dto| {
let node_id = node_dto.node_id;
let http_api_port = node_dto.http_api_port;
match SkimmedNode::try_from(node_dto) {
Ok(node) => Some((node, http_api_port)),
Err(e) => {
tracing::error!("Failed to decode node_id={}: {}", node_id, e);
None
@@ -33,7 +34,7 @@ pub(crate) async fn get_nodes_for_scraping(pool: &DbPool) -> Result<Vec<ScraperN
})
})?;
skimmed_nodes.for_each(|node| {
skimmed_nodes.for_each(|(node, http_api_port)| {
// TODO: relies on polyfilling: Nym nodes table might contain legacy mixnodes
// as well. Categorize them here.
let node_kind = if gateway_keys.contains(&node.ed25519_identity_pubkey.to_base58_string()) {
@@ -54,7 +55,7 @@ pub(crate) async fn get_nodes_for_scraping(pool: &DbPool) -> Result<Vec<ScraperN
.into_iter()
.map(|ip| ip.to_string())
.collect::<Vec<_>>(),
http_api_port: node.mix_port.into(),
http_api_port: http_api_port.map(|port| port as u16),
})
});
@@ -138,6 +138,7 @@ mod db_tests {
performance: "1.0".to_string(),
self_described: None,
bond_info: None,
http_api_port: None,
};
let skimmed_node: nym_validator_client::nym_api::SkimmedNode =
@@ -362,22 +363,42 @@ fn test_scraper_node_info_contact_addresses() {
let node_info = ScraperNodeInfo {
node_kind: ScrapeNodeKind::MixingNymNode { node_id: 123 },
hosts: vec!["1.1.1.1".to_string(), "example.com".to_string()],
http_api_port: 8080,
http_api_port: None,
};
let addresses = node_info.contact_addresses();
// Should generate multiple URLs for each host
// Custom port (8080) should be inserted at the beginning
// When no custom port is specified only default ports should be used
assert!(addresses.contains(&"http://1.1.1.1:8080".to_string()));
assert!(addresses.contains(&"http://example.com:8080".to_string()));
assert!(addresses.contains(&"http://1.1.1.1:8000".to_string()));
assert!(addresses.contains(&"https://1.1.1.1".to_string()));
assert!(addresses.contains(&"http://1.1.1.1".to_string()));
assert!(addresses.contains(&"http://example.com:8000".to_string()));
// Check that URLs follow the expected pattern
assert!(addresses.len() >= 8); // At least 4 URLs per host
}
#[test]
fn test_scraper_node_info_contact_addresses_with_custom_http_api_port() {
use crate::db::models::{ScrapeNodeKind, ScraperNodeInfo};
let node_info = ScraperNodeInfo {
node_kind: ScrapeNodeKind::MixingNymNode { node_id: 123 },
hosts: vec!["1.1.1.1".to_string(), "example.com".to_string()],
http_api_port: Some(4444),
};
let addresses = node_info.contact_addresses();
// Should generate multiple URLs for each host
// Custom port (4444) should be the only port in the list
assert!(addresses.contains(&"http://1.1.1.1:4444".to_string()));
assert!(addresses.contains(&"http://example.com:4444".to_string()));
// Check that URLs follow the expected pattern
assert!(addresses.len() >= 2); // At least 4 URLs per host
}
#[test]
fn test_scrape_node_kind_node_id() {
use crate::db::models::ScrapeNodeKind;
@@ -414,6 +435,7 @@ fn test_nym_node_dto_with_invalid_keys() {
performance: "1.0".to_string(),
self_described: None,
bond_info: None,
http_api_port: None,
};
let result: Result<nym_validator_client::nym_api::SkimmedNode, _> = nym_node_dto.try_into();
@@ -451,6 +473,7 @@ fn test_nym_node_dto_with_invalid_performance() {
performance: "invalid_percent".to_string(),
self_described: None,
bond_info: None,
http_api_port: None,
};
let result: Result<nym_validator_client::nym_api::SkimmedNode, _> = nym_node_dto.try_into();
@@ -1,4 +1,6 @@
use crate::cli::Commands;
use crate::monitor::DelegationsCache;
use crate::node_scraper::helpers::scrape_and_store_description_by_node_id;
use crate::ticketbook_manager::TicketbookManager;
use crate::ticketbook_manager::state::TicketbookManagerState;
use clap::Parser;
@@ -40,11 +42,49 @@ async fn main() -> anyhow::Result<()> {
tracing::info!("Registered {} agent keys", agent_key_list.len());
let connection_url = args.database_url.clone();
tracing::debug!("Using config:\n{:#?}", args);
if std::env::var("SHOW_CONFIG").ok().is_some() {
tracing::debug!("Using config:\n{:#?}", args);
}
let storage = db::Storage::init(connection_url, args.sqlx_busy_timeout_s).await?;
let db_pool = storage.pool_owned();
// node geocache is shared between node monitor and HTTP server
let geocache = moka::future::Cache::builder()
.time_to_live(args.geodata_ttl)
.build();
let delegations_cache = DelegationsCache::new();
let client_config = nym_validator_client::nyxd::Config::try_from_nym_network_details(
&nym_network_defaults::NymNetworkDetails::new_from_env(),
)?;
let nyxd_client = NyxdClient::connect(client_config.clone(), args.nyxd_addr.as_str())
.map_err(|err| anyhow::anyhow!("Couldn't connect: {}", err))?;
match args.command {
Some(Commands::ScrapeNode { node_id }) => {
if std::env::var("RUN_ONCE_INIT_NODES").ok().is_some() {
let geocache_clone = geocache.clone();
let delegations_cache_clone = Arc::clone(&delegations_cache);
monitor::run_once(
db_pool.clone(),
args.nym_api_client_timeout,
nyxd_client,
args.ipinfo_api_token,
geocache_clone,
delegations_cache_clone,
)
.await?;
}
tracing::info!("Scraping node with id {node_id}...");
scrape_and_store_description_by_node_id(&db_pool, node_id).await?;
return Ok(());
}
None => {
// default behaviour
}
}
// Start the node scraper
let scraper = node_scraper::DescriptionScraper::new(storage.pool_owned());
shutdown_manager.spawn_with_shutdown(async move {
@@ -58,20 +98,9 @@ async fn main() -> anyhow::Result<()> {
scraper.start().await;
});
// node geocache is shared between node monitor and HTTP server
let geocache = moka::future::Cache::builder()
.time_to_live(args.geodata_ttl)
.build();
let delegations_cache = DelegationsCache::new();
// Start the monitor
let geocache_clone = geocache.clone();
let delegations_cache_clone = Arc::clone(&delegations_cache);
let client_config = nym_validator_client::nyxd::Config::try_from_nym_network_details(
&nym_network_defaults::NymNetworkDetails::new_from_env(),
)?;
let nyxd_client = NyxdClient::connect(client_config.clone(), args.nyxd_addr.as_str())
.map_err(|err| anyhow::anyhow!("Couldn't connect: {}", err))?;
shutdown_manager.spawn_with_shutdown(async move {
monitor::run_in_background(
@@ -68,7 +68,7 @@ pub(crate) async fn run_in_background(
loop {
tracing::info!("Refreshing node info...");
if let Err(e) = monitor.run().await {
if let Err(e) = monitor.run(false).await {
tracing::error!(
"Monitor run failed: {e}, retrying in {}s...",
MONITOR_FAILURE_RETRY_DELAY.as_secs()
@@ -84,8 +84,33 @@ pub(crate) async fn run_in_background(
}
}
#[instrument(level = "debug", name = "data_monitor", skip_all)]
pub(crate) async fn run_once(
db_pool: DbPool,
nym_api_client_timeout: Duration,
nyxd_client: nym_validator_client::QueryHttpRpcNyxdClient,
ipinfo_api_token: String,
geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
) -> anyhow::Result<()> {
let ipinfo = IpInfoClient::new(ipinfo_api_token.clone());
let mut monitor = Monitor {
db_pool,
network_details: nym_network_defaults::NymNetworkDetails::new_from_env(),
nym_api_client_timeout,
nyxd_client,
ipinfo,
geocache,
node_delegations,
};
tracing::info!("Refreshing node info...");
monitor.run(true).await
}
impl Monitor {
async fn run(&mut self) -> anyhow::Result<()> {
async fn run(&mut self, exit_early: bool) -> anyhow::Result<()> {
self.check_ipinfo_bandwidth().await;
let default_api_url = self
@@ -153,6 +178,11 @@ impl Monitor {
tracing::debug!("{} nym nodes written to DB!", inserted);
})?;
// stop here if running once
if exit_early {
return Ok(());
}
// refresh geodata for all nodes
for node_description in described_nodes.values() {
self.location_cached(node_description).await;
@@ -118,6 +118,17 @@ pub fn sanitize_description(
}
}
pub async fn scrape_and_store_description_by_node_id(pool: &DbPool, node_id: i64) -> Result<()> {
let nodes = crate::db::queries::get_nodes_for_scraping(pool).await?;
match nodes.iter().find(|n| *n.node_kind.node_id() == node_id) {
Some(node) => Ok(scrape_and_store_description(pool, node.clone()).await?),
None => {
error!("Could not find node with id {node_id}");
Err(anyhow!("Could not find node with id {node_id}"))
}
}
}
pub async fn scrape_and_store_description(pool: &DbPool, node: ScraperNodeInfo) -> Result<()> {
let client = build_client()?;
let urls = node.contact_addresses();
@@ -152,7 +163,13 @@ pub async fn scrape_and_store_description(pool: &DbPool, node: ScraperNodeInfo)
anyhow::anyhow!("Failed to fetch description from any URL: {}", err_msg)
})?;
let sanitized_description = sanitize_description(description, *node.node_id());
let sanitized_description = sanitize_description(description.clone(), *node.node_id());
trace!("tried_url_list = {tried_url_list:?}");
trace!("ndoe_id = {}", node.node_id());
trace!("description = {:?}", description);
trace!("sanitized_description = {:?}", sanitized_description);
insert_scraped_node_description(pool, &node.node_kind, &sanitized_description).await?;
Ok(())
+49 -100
View File
@@ -9,7 +9,6 @@ use nym_credentials_interface::TicketType;
use nym_ip_packet_client::IprClientConnect;
use nym_registration_common::AssignedAddresses;
use nym_sdk::mixnet::{EventReceiver, MixnetClient, Recipient};
use tracing::debug;
use crate::config::RegistrationClientConfig;
@@ -35,49 +34,23 @@ pub struct RegistrationClient {
event_rx: EventReceiver,
}
// Bundle of an actual error and the underlying mixnet client so it can be shutdown correctly if needed
struct RegistrationError {
mixnet_client: Option<MixnetClient>,
source: crate::RegistrationClientError,
}
impl RegistrationClient {
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationError> {
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationClientError> {
let entry_mixnet_gateway_ip = self.config.entry.node.ip_address;
let exit_mixnet_gateway_ip = self.config.exit.node.ip_address;
let Some(ipr_address) = self.config.exit.node.ipr_address else {
return Err(RegistrationError {
mixnet_client: Some(self.mixnet_client),
source: RegistrationClientError::NoIpPacketRouterAddress {
node_id: self.config.exit.node.identity.to_base58_string(),
},
});
};
let ipr_address = self.config.exit.node.ipr_address.ok_or(
RegistrationClientError::NoIpPacketRouterAddress {
node_id: self.config.exit.node.identity.to_base58_string(),
},
)?;
let mut ipr_client =
IprClientConnect::new(self.mixnet_client, self.cancel_token.child_token());
let interface_addresses = match self
.cancel_token
.run_until_cancelled(ipr_client.connect(ipr_address))
IprClientConnect::new(self.mixnet_client, self.cancel_token.clone()).await;
let interface_addresses = ipr_client
.connect(ipr_address)
.await
{
Some(Ok(addr)) => addr,
Some(Err(e)) => {
return Err(RegistrationError {
mixnet_client: Some(ipr_client.into_mixnet_client()),
source: RegistrationClientError::ConnectToIpPacketRouter(e),
});
}
None => {
return Err(RegistrationError {
mixnet_client: Some(ipr_client.into_mixnet_client()),
source: RegistrationClientError::Cancelled,
});
}
};
.map_err(RegistrationClientError::ConnectToIpPacketRouter)?;
Ok(RegistrationResult::Mixnet(Box::new(
MixnetRegistrationResult {
@@ -94,24 +67,18 @@ impl RegistrationClient {
)))
}
async fn register_wg(self) -> Result<RegistrationResult, RegistrationError> {
let Some(entry_auth_address) = self.config.entry.node.authenticator_address else {
return Err(RegistrationError {
mixnet_client: Some(self.mixnet_client),
source: RegistrationClientError::AuthenticationNotPossible {
node_id: self.config.entry.node.identity.to_base58_string(),
},
});
};
async fn register_wg(self) -> Result<RegistrationResult, RegistrationClientError> {
let entry_auth_address = self.config.entry.node.authenticator_address.ok_or(
RegistrationClientError::AuthenticationNotPossible {
node_id: self.config.entry.node.identity.to_base58_string(),
},
)?;
let Some(exit_auth_address) = self.config.exit.node.authenticator_address else {
return Err(RegistrationError {
mixnet_client: Some(self.mixnet_client),
source: RegistrationClientError::AuthenticationNotPossible {
node_id: self.config.exit.node.identity.to_base58_string(),
},
});
};
let exit_auth_address = self.config.exit.node.authenticator_address.ok_or(
RegistrationClientError::AuthenticationNotPossible {
node_id: self.config.exit.node.identity.to_base58_string(),
},
)?;
let entry_version = self.config.entry.node.version;
tracing::debug!("Entry gateway version: {entry_version}");
@@ -120,10 +87,8 @@ impl RegistrationClient {
// Start the auth client mixnet listener, which will listen for incoming messages from the
// mixnet and rebroadcast them to the auth clients.
// From this point on, we don't need to care about the mixnet client anymore
let mixnet_listener =
AuthClientMixnetListener::new(self.mixnet_client, self.cancel_token.child_token())
.start();
AuthClientMixnetListener::new(self.mixnet_client, self.cancel_token.clone()).start();
let mut entry_auth_client = AuthenticatorClient::new(
mixnet_listener.subscribe(),
@@ -150,33 +115,24 @@ impl RegistrationClient {
let exit_fut = exit_auth_client
.register_wireguard(&*self.bandwidth_controller, TicketType::V1WireguardExit);
let (entry, exit) = Box::pin(
self.cancel_token
.run_until_cancelled(async { tokio::join!(entry_fut, exit_fut) }),
)
.await
.ok_or(RegistrationError {
mixnet_client: None,
source: RegistrationClientError::Cancelled,
})?;
let (entry, exit) = Box::pin(async { tokio::join!(entry_fut, exit_fut) }).await;
let entry = entry.map_err(|source| RegistrationError {
mixnet_client: None,
source: RegistrationClientError::EntryGatewayRegisterWireguard {
gateway_id: self.config.entry.node.identity.to_base58_string(),
authenticator_address: Box::new(entry_auth_address),
source: Box::new(source),
},
})?;
let exit = exit.map_err(|source| RegistrationError {
mixnet_client: None,
source: RegistrationClientError::EntryGatewayRegisterWireguard {
gateway_id: self.config.exit.node.identity.to_base58_string(),
authenticator_address: Box::new(exit_auth_address),
source: Box::new(source),
},
})?;
let entry =
entry.map_err(
|source| RegistrationClientError::EntryGatewayRegisterWireguard {
gateway_id: self.config.entry.node.identity.to_base58_string(),
authenticator_address: Box::new(entry_auth_address),
source: Box::new(source),
},
)?;
let exit =
exit.map_err(
|source| RegistrationClientError::ExitGatewayRegisterWireguard {
gateway_id: self.config.exit.node.identity.to_base58_string(),
authenticator_address: Box::new(exit_auth_address),
source: Box::new(source),
},
)?;
Ok(RegistrationResult::Wireguard(Box::new(
WireguardRegistrationResult {
@@ -191,23 +147,16 @@ impl RegistrationClient {
}
pub async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
let registration_result = if self.config.two_hops {
self.register_wg().await
} else {
self.register_mix_exit().await
};
// If we failed to register, and we were the owner of the mixnet client, shut it down
match registration_result {
Ok(result) => Ok(result),
Err(error) => {
debug!("Registration failed");
if let Some(mixnet_client) = error.mixnet_client {
debug!("Shutting down mixnet client");
mixnet_client.disconnect().await;
self.cancel_token
.clone()
.run_until_cancelled(async {
if self.config.two_hops {
self.register_wg().await
} else {
self.register_mix_exit().await
}
Err(error.source)
}
}
})
.await
.ok_or(RegistrationClientError::Cancelled)?
}
}