Compare commits

...

10 Commits

Author SHA1 Message Date
dynco-nym b9339b8f0c Add /status endpoints (#5857)
* Add /status endpoints

* Bump package version

* pub use instead of import
2025-06-16 13:19:35 +02:00
Andrej Mihajlov 43a7360399 Merge pull request #5856 from nymtech/am/remove-surb-screaming-logs
Clear out screaming logs
2025-06-16 11:39:27 +02:00
Andrej Mihajlov 5f9f7f0fac Clear out screaming logs 2025-06-13 11:00:48 +02:00
Andrej Mihajlov df0e2fe489 Merge pull request #5853 from nymtech/am/path-display
Use display when printing paths
2025-06-13 10:54:12 +02:00
benedetta davico bc33cc4c8d Merge pull request #5855 from nymtech/fix-qa-removal 2025-06-13 09:40:56 +02:00
Simon Wicky a31597aca9 fix removal of qa env 2025-06-13 09:30:00 +02:00
Jack Wampler 378229b04e HTTP Discovery objects & network defaults (#5814)
add extended (optional) fields to the NetworkDiscovery and configure fallback hosts
2025-06-12 11:15:36 -06:00
Andrej Mihajlov fec196c097 Use display when printing paths 2025-06-12 17:17:00 +02:00
import this d6b3d7fc0a [DOCs/operators]: Release notes for v2025.11 cheddar (#5852)
* bump up version

* add dev features

* add operator updates

* add updated stats

* update prebuild
2025-06-12 11:19:00 +00:00
dynco-nym ac273480f8 Fix CI version check (#5851)
* Fix version

* Test .rc version

* Undo cargo.toml version

* Remove comment

* Apply to statistics service
2025-06-12 11:17:56 +02:00
20 changed files with 226 additions and 26 deletions
@@ -44,8 +44,10 @@ jobs:
echo "Tag is empty"
exit 1
fi
# first, list all tags for logging purposes
curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq --arg tag $TAG '.tags | contains([$tag])' )
# check if there's a matching tag
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq -r --arg tag "$TAG" 'any(.tags[]; . == $tag)' )
if [[ $exists = "true" ]]; then
echo "Version '$TAG' defined in Cargo.toml ALREADY EXISTS as tag in harbor repo"
exit 1
@@ -53,5 +55,5 @@ jobs:
echo "Version '$TAG' doesn't exist on the remote"
else
echo "Unknown output '$exists'"
exit 1
exit 2
fi
@@ -44,8 +44,10 @@ jobs:
echo "Tag is empty"
exit 1
fi
# first, list all tags for logging purposes
curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq --arg tag $TAG '.tags | contains([$tag])' )
# check if there's a matching tag
exists=$(curl -su ${{ secrets.HARBOR_ROBOT_USERNAME }}:${{ secrets.HARBOR_ROBOT_SECRET }} "$registry/v2/$repo_name/tags/list" | jq -r --arg tag "$TAG" 'any(.tags[]; . == $tag)' )
if [[ $exists = "true" ]]; then
echo "Version '$TAG' defined in Cargo.toml ALREADY EXISTS as tag in harbor repo"
exit 1
@@ -53,5 +55,5 @@ jobs:
echo "Version '$TAG' doesn't exist on the remote"
else
echo "Unknown output '$exists'"
exit 1
exit 2
fi
Generated
+1 -1
View File
@@ -6649,7 +6649,7 @@ dependencies = [
[[package]]
name = "nym-node-status-api"
version = "3.0.0"
version = "3.1.0"
dependencies = [
"ammonia",
"anyhow",
@@ -33,7 +33,6 @@ where
self.backend.load_surb_storage().await
}
// this will have to get enabled after merging develop
pub async fn flush_on_shutdown(
mut self,
mem_state: CombinedReplyStorage,
@@ -50,7 +49,6 @@ where
shutdown.recv().await;
info!("PersistentReplyStorage is flushing all reply-related data to underlying storage");
info!("you MUST NOT forcefully shutdown now or you risk data corruption!");
if let Err(err) = self.backend.flush_surb_storage(&mem_state).await {
error!("failed to flush our reply-related data to the persistent storage: {err}")
} else {
@@ -54,8 +54,8 @@ impl PersistentStorage {
/// * `database_path`: path to the database.
pub async fn init<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
debug!(
"Attempting to connect to database {:?}",
database_path.as_ref().as_os_str()
"Attempting to connect to database {}",
database_path.as_ref().display()
);
let opts = sqlx::sqlite::SqliteConnectOptions::new()
+2 -2
View File
@@ -33,8 +33,8 @@ impl PersistentStatsStorage {
/// * `database_path`: path to the database.
pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StatsStorageError> {
debug!(
"Attempting to connect to database {:?}",
database_path.as_ref().as_os_str()
"Attempting to connect to database {}",
database_path.as_ref().display()
);
// TODO: we can inject here more stuff based on our gateway global config
+2 -2
View File
@@ -82,8 +82,8 @@ impl GatewayStorage {
message_retrieval_limit: i64,
) -> Result<Self, GatewayStorageError> {
debug!(
"Attempting to connect to database {:?}",
database_path.as_ref().as_os_str()
"Attempting to connect to database {}",
database_path.as_ref().display()
);
// TODO: we can inject here more stuff based on our gateway global config
+28 -2
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "network")]
use crate::{DenomDetails, ValidatorDetails};
use crate::{ApiUrlConst, DenomDetails, ValidatorDetails};
pub const NETWORK_NAME: &str = "mainnet";
@@ -29,10 +29,36 @@ pub const COCONUT_DKG_CONTRACT_ADDRESS: &str =
pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub const NYXD_URL: &str = "https://rpc.nymtech.net";
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
pub const NYXD_WS: &str = "wss://rpc.nymtech.net/websocket";
pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/";
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
#[cfg(feature = "network")]
pub const NYM_APIS: &[ApiUrlConst] = &[
ApiUrlConst {
url: NYM_API,
front_hosts: None,
},
ApiUrlConst {
url: "https://nym-fronntdoor.vercel.app/api/",
front_hosts: Some(&["vercel.app", "vercel.com"]),
},
ApiUrlConst {
url: "https://nym-frontdoor.global.ssl.fastly.net/api/",
front_hosts: Some(&["yelp.global.ssl.fastly.net"]),
},
];
pub const NYM_VPN_API: &str = "https://nymvpn.com/api/";
#[cfg(feature = "network")]
pub const NYM_VPN_APIS: &[ApiUrlConst] = &[
ApiUrlConst {
url: NYM_VPN_API,
front_hosts: Some(&["vercel.app", "vercel.com"]),
},
ApiUrlConst {
url: "https://nymvpn-frontdoor.global.ssl.fastly.net/api/",
front_hosts: Some(&["yelp.global.ssl.fastly.net"]),
},
];
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
pub const EXIT_POLICY_URL: &str =
+35
View File
@@ -37,6 +37,37 @@ pub struct NymNetworkDetails {
pub contracts: NymContracts,
pub explorer_api: Option<String>,
pub nym_vpn_api_url: Option<String>,
pub nym_api_urls: Option<Vec<ApiUrl>>,
pub nym_vpn_api_urls: Option<Vec<ApiUrl>>,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct ApiUrl {
/// Expects a string formatted Url
///
/// see https://docs.rs/url/latest/url/struct.Url.html
pub url: String,
/// Optional alternative equivalent hostnames. Each entry must parse as valid Host
///
/// see https://docs.rs/url/latest/url/enum.Host.html
pub front_hosts: Option<Vec<String>>,
}
pub struct ApiUrlConst<'a> {
pub url: &'a str,
pub front_hosts: Option<&'a [&'a str]>,
}
impl From<ApiUrlConst<'_>> for ApiUrl {
fn from(value: ApiUrlConst) -> Self {
ApiUrl {
url: value.url.to_string(),
front_hosts: value
.front_hosts
.map(|slice| slice.iter().map(|s| s.to_string()).collect()),
}
}
}
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
@@ -67,6 +98,8 @@ impl NymNetworkDetails {
contracts: Default::default(),
explorer_api: Default::default(),
nym_vpn_api_url: Default::default(),
nym_api_urls: Default::default(),
nym_vpn_api_urls: Default::default(),
}
}
@@ -154,6 +187,8 @@ impl NymNetworkDetails {
},
explorer_api: parse_optional_str(mainnet::EXPLORER_API),
nym_vpn_api_url: parse_optional_str(mainnet::NYM_VPN_API),
nym_api_urls: None,
nym_vpn_api_urls: None,
}
}
@@ -1 +1 @@
Friday, June 6th 2025, 09:33:10 UTC
Thursday, June 12th 2025, 11:03:30 UTC
@@ -47,6 +47,53 @@ This page displays a full list of all the changes during our release cycle from
<VarInfo />
## `v2025.11-cheddar`
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.11-cheddar)
- [`nym-node`](nodes/nym-node.mdx) version `1.13.0`
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2025-06-10T09:41:31.291089877Z
Build Version: 1.13.0
Commit SHA: ef220882d4bcf0a8a703ea62f9273e017c9ebb51
Commit Date: 2025-06-10T11:39:20.000000000+02:00
Commit Branch: HEAD
rustc Version: 1.86.0
rustc Channel: stable
cargo Profile: release
```
### Operators Updates & Tools
- **Nym Improvement Proposals**: NIPs are being introduced, allowing us to propose changes to the Nym network and ecosystem, and decentralise governance
- **NIP1** will outline the governance process: Its draft will be published soon for discussion
- **NIP2** will be about reducing the stake saturation point variable, which would make reward distribution across the network more equitable
- **Join the Operator AMA next Tuesday** for an in-depth overview of what this all means (feat Nym Chief Scientist Claudia Diaz)
- Keep an eye on our channels for information about how to participate in the upcoming voting (no need for forum registration, we are building something new!)
### Features
- [Track wireguard credential retries](https://github.com/nymtech/nym/pull/5783)
- [Swap a decode into a `fromrow` to please future `postgres` feature](https://github.com/nymtech/nym/pull/5785): This PR change a sqlx derive from `Decode` to `FromRow` because a future PR will bring the `postgres` feature and it doesn't like that `Decode` there
- [Fix contains ticketbook function that always returned true](https://github.com/nymtech/nym/pull/5787)
- [QoL: `RequestPath` trait for `http-api-client`](https://github.com/nymtech/nym/pull/5788): Those `PathSegments` in `ApiClient` weren't very user-friendly. Instead we've defined a `RequestPath` trait that allows you to also pass a good old `&str` or `String` and internally it does exactly the same sanitization as before. `PathSegments` are also fully supported to not break any existing compatibility.
- [Nym Statistics API](https://github.com/nymtech/nym/pull/5800):
- Types for `nym-vpn-client`: It introduces `VpnClientStatsReport` which will be used by nym vpn clients to report statistics. Those types are in the monorepo because they're used by the `num-statistics-API`
- Nym Statistics API: Basically a `REST API` with a `postgres` backend. In this first iteration, it only accepts `VpnClientStatsReport` and stores them in its database. It optionally connects to the Nym API to confirm reports are coming from the network and attach country code to them if it's the case (exit node country code).
- [Resolve 1.87 clippy warnings](https://github.com/nymtech/nym/pull/5802)
- [Adjusted wallet storybook mocks to fix the build](https://github.com/nymtech/nym/pull/5804)
- [Set cached storage counters to 0](https://github.com/nymtech/nym/pull/5812)
- [No autoremoval of peers](https://github.com/nymtech/nym/pull/5831)
## `v2025.10-brie`
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.10-brie)
@@ -18,12 +18,12 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
## Current version
```sh
nym-node
ym-node
Binary Name: nym-node
Build Timestamp: 2025-05-27T10:13:18.511075453Z
Build Version: 1.12.0
Commit SHA: 1c6db86259d08d80e8bcfbc4fcc71ccb147fcfd0
Commit Date: 2025-05-27T12:11:13.000000000+02:00
Build Timestamp: 2025-06-10T09:41:31.291089877Z
Build Version: 1.13.0
Commit SHA: ef220882d4bcf0a8a703ea62f9273e017c9ebb51
Commit Date: 2025-06-10T11:39:20.000000000+02:00
Commit Branch: HEAD
rustc Version: 1.86.0
rustc Channel: stable
@@ -3,7 +3,7 @@
[package]
name = "nym-node-status-api"
version = "3.0.0"
version = "3.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -28,7 +28,7 @@ moka = { workspace = true, features = ["future"] }
# Nym API: revert after Cheddar is out
nym-contracts-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-mixnet-contract-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-bin-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-bin-common = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar", features = ["openapi"]}
nym-node-status-client = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-crypto = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
nym-http-api-client = { git = "https://github.com/nymtech/nym.git", branch = "release/2025.11-cheddar" }
@@ -14,6 +14,7 @@ pub(crate) mod metrics;
pub(crate) mod mixnodes;
pub(crate) mod nym_nodes;
pub(crate) mod services;
pub(crate) mod status;
pub(crate) mod summary;
pub(crate) mod testruns;
@@ -39,7 +40,8 @@ impl RouterBuilder {
.nest("/mixnodes", mixnodes::routes())
.nest("/services", services::routes())
.nest("/summary", summary::routes())
.nest("/metrics", metrics::routes()),
.nest("/metrics", metrics::routes())
.nest("/status", status::routes()),
)
.nest(
"/explorer/v3",
@@ -0,0 +1,46 @@
use axum::{extract::State, Json, Router};
use nym_validator_client::models::BinaryBuildInformationOwned;
use tracing::instrument;
use crate::http::{
error::HttpResult,
state::{AppState, HealthInfo},
};
pub(crate) fn routes() -> Router<AppState> {
Router::new()
.route("/build_information", axum::routing::get(build_information))
.route("/health", axum::routing::get(health))
}
#[utoipa::path(
tag = "Status",
get,
path = "/build_information",
context_path = "/v2/status",
responses(
(status = 200, body = BinaryBuildInformationOwned)
)
)]
#[instrument(level = tracing::Level::INFO, skip_all)]
async fn build_information(
State(state): State<AppState>,
) -> HttpResult<Json<BinaryBuildInformationOwned>> {
let build_info = state.build_information().to_owned();
Ok(Json(build_info))
}
#[utoipa::path(
tag = "Status",
get,
path = "/health",
context_path = "/v2/status",
responses(
(status = 200, body = HealthInfo)
)
)]
#[instrument(level = tracing::Level::INFO, skip_all)]
async fn health(State(state): State<AppState>) -> HttpResult<Json<HealthInfo>> {
Ok(Json(state.health()))
}
@@ -1,15 +1,20 @@
use cosmwasm_std::Decimal;
use itertools::Itertools;
use moka::{future::Cache, Entry};
use nym_bin_common::bin_info_owned;
use nym_contracts_common::NaiveFloat;
use nym_crypto::asymmetric::ed25519::PublicKey;
use nym_mixnet_contract_common::NodeId;
use nym_validator_client::nym_api::SkimmedNode;
use semver::Version;
use serde::Serialize;
use std::{collections::HashMap, sync::Arc, time::Duration};
use time::UtcDateTime;
use tokio::sync::RwLock;
use tracing::{error, instrument, warn};
use utoipa::ToSchema;
use super::models::SessionStats;
use crate::{
db::{queries, DbPool},
http::models::{
@@ -18,7 +23,7 @@ use crate::{
monitor::{DelegationsCache, NodeGeoCache},
};
use super::models::SessionStats;
pub(crate) use nym_validator_client::models::BinaryBuildInformationOwned;
#[derive(Debug, Clone)]
pub(crate) struct AppState {
@@ -28,6 +33,7 @@ pub(crate) struct AppState {
agent_max_count: i64,
node_geocache: NodeGeoCache,
node_delegations: Arc<RwLock<DelegationsCache>>,
bin_info: BinaryInfo,
}
impl AppState {
@@ -46,6 +52,7 @@ impl AppState {
agent_max_count,
node_geocache,
node_delegations,
bin_info: BinaryInfo::new(),
}
}
@@ -78,6 +85,15 @@ impl AppState {
.await
.delegations_owned(node_id)
}
pub(crate) fn health(&self) -> HealthInfo {
let uptime = (UtcDateTime::now() - self.bin_info.startup_time).whole_seconds();
HealthInfo { uptime }
}
pub(crate) fn build_information(&self) -> &BinaryBuildInformationOwned {
&self.bin_info.build_info
}
}
static GATEWAYS_LIST_KEY: &str = "gateways";
@@ -631,3 +647,23 @@ async fn aggregate_node_info_from_db(
Ok(parsed_nym_nodes)
}
#[derive(Debug, Clone)]
pub(crate) struct BinaryInfo {
startup_time: UtcDateTime,
build_info: BinaryBuildInformationOwned,
}
impl BinaryInfo {
fn new() -> Self {
Self {
startup_time: UtcDateTime::now(),
build_info: bin_info_owned!(),
}
}
}
#[derive(Serialize, ToSchema)]
pub(crate) struct HealthInfo {
uptime: i64,
}
@@ -55,5 +55,7 @@ pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails {
},
explorer_api: parse_optional_str(EXPLORER_API),
nym_vpn_api_url: None,
nym_vpn_api_urls: None,
nym_api_urls: None,
}
}
+1 -1
View File
@@ -459,7 +459,7 @@ impl fmt::Display for OptionalValidators {
.as_ref()
.map(|validators| format!(",\nsandbox: [\n{}\n]", validators.iter().format("\n")))
.unwrap_or_default();
write!(f, "{s1}{s2}{s3}")
write!(f, "{s1}{s2}")
}
}
@@ -83,6 +83,8 @@ impl<'a> From<&'a LoadedNetwork> for nym_config::defaults::NymNetworkDetails {
contracts,
explorer_api: None,
nym_vpn_api_url: None,
nym_vpn_api_urls: None,
nym_api_urls: None,
}
}
}
@@ -40,6 +40,8 @@ impl InitCtx {
contracts: Default::default(),
explorer_api: None,
nym_vpn_api_url: None,
nym_vpn_api_urls: None,
nym_api_urls: None,
};
Ok(Config::try_from_nym_network_details(&network_details)?)
}