Compare commits

...

120 Commits

Author SHA1 Message Date
Zane Schepke 8e43a5ce1d Update sign.sh 2023-11-27 04:44:43 -05:00
Zane Schepke ef30cafaa4 Update sign.sh 2023-11-27 03:31:23 -05:00
Zane Schepke 80f81f1c4a Update sign.sh 2023-11-27 00:02:31 -05:00
Zane Schepke 3e98fee06e Update sign.sh 2023-11-26 20:55:50 -05:00
Zane Schepke 099d23b568 update bundle id and package 2023-11-24 12:49:28 -05:00
Zane Schepke 874aecb0a4 Update sign.sh 2023-11-24 09:04:22 -05:00
Zane Schepke acf9de0f74 Add protoc optionals arg 2023-11-24 06:56:13 -05:00
Zane Schepke 064624d8ec use x86 for signtool 2023-11-24 06:39:28 -05:00
Zane Schepke ced32da018 Update sign.sh 2023-11-23 18:23:50 -05:00
Zane Schepke ad5c0991c0 Add desktop vpn app project 2023-11-20 08:51:23 -05:00
Zane Schepke 2c63518735 Merge branch 'develop' of https://github.com/nymtech/nym into develop 2023-11-16 08:57:21 -05:00
Jon Häggblad ac66906980 IPR: add exit policy (#4127)
* Copy over request_filter

* Comment out stuff we don't need

* Delete unused allowed_hosts

* Delete unused code in request_filter

* Setup request filter

* Handle address checks

* rustfmt

* Tweak errors

* clippy

* allow dead code for non-linux

* inline log_msg

* Add ParsedPacket type
2023-11-16 14:13:13 +01:00
Gala afd9f823d8 Merge pull request #4151 from nymtech/feat/explorer-vpnsite-buttom
Feat/explorer vpnsite buttom
2023-11-16 13:08:06 +01:00
serinko d818448848 DOC: hotfix 2023-11-16 12:07:20 +00:00
Gala a9a1ba2847 please lint.. 2023-11-16 12:50:10 +01:00
Drazen Urch 2708c0ce10 Feature/deb package (#4153)
* Add debian scaffolding, allow specifying home_dir in env

* Run as nym user
2023-11-16 12:35:02 +01:00
Gala bb3e9b3d4e remove non used variable 2023-11-16 12:33:43 +01:00
Gala e624f42ad5 fixing build 2023-11-16 12:03:23 +01:00
mx 7da83397dd Merge pull request #4152 from nymtech/feature/docs/sort-info
DOCs: Operators - create tables to clarify Smoosh progress
2023-11-16 10:18:00 +00:00
serinko 26d0b4b159 create tables to clarify Smoosh progress 2023-11-16 11:12:40 +01:00
mx b74490dc50 Merge pull request #4150 from nymtech/feature/ts-sdk-fixes
adding SURBs info to mixfetch
2023-11-15 17:19:59 +00:00
Jędrzej Stuczyński 8113095ff5 remove needless borrow (#4149) 2023-11-15 16:34:32 +01:00
Gala 8339d6ab49 nymvpn link on footer 2023-11-15 16:20:14 +01:00
Gala f037b2ae68 adding nymvpn link to explorer 2023-11-15 15:58:31 +01:00
Zane Schepke 2c32ce8cf0 Update README.md 2023-11-15 09:39:18 -05:00
Zane Schepke 2a4c1d96a4 Update README.md 2023-11-15 09:37:21 -05:00
Lorexia ed04ddf1c4 adding SURBs info to mixfetch 2023-11-15 15:35:10 +01:00
Zane Schepke 34b5d66df6 Update README.md 2023-11-15 09:35:04 -05:00
Tommy Verrall 0a1a5c25f7 Merge pull request #4148 from nymtech/chore/add-update-cost-params
Add update cost params to the NYM-CLI
2023-11-15 13:58:07 +00:00
Jędrzej Stuczyński 6bdba7046f Bugfix/prerelease versionbump (#4145)
* prerelease updating rc suffix

* added post-run summary

* updated error message
2023-11-15 13:58:21 +01:00
Tommy Verrall 428d91a536 fmt 2023-11-15 12:37:01 +00:00
Tommy Verrall 88e0eaafcb update args to pass through correctly 2023-11-15 12:32:23 +00:00
Tommy Verrall dd19cabf15 adding the cost parameter update to the nym-cli 2023-11-15 12:30:02 +00:00
mx 4ec08da36d Merge pull request #4144 from nymtech/hackathon-submission
linked to discussion fr submission
2023-11-15 09:15:30 +00:00
mfahampshire 16c59d95d3 linked to discussion fr submission 2023-11-15 10:00:27 +01:00
Jon Häggblad e6f76380f6 Add timeouts in tun handler (#4142) 2023-11-14 17:34:37 +01:00
serinko 6961ecae55 hotfix
Adding a white line to fix the re-appearing bug
2023-11-14 15:06:49 +00:00
serinko dd814c067c Merge pull request #4138 from nymtech/patch/docs/hotfix
DOCS hot-fix: Missed bugs, details, spellcheck etc
2023-11-14 14:37:10 +00:00
serinko 666d5945b9 add init to fix node family cmdrun output 2023-11-14 15:15:15 +01:00
serinko ecebf6e84c add cosmwasm time execution definition 2023-11-14 14:54:45 +01:00
serinko 4663d39505 fix naming 2023-11-14 14:31:39 +01:00
serinko 81a7d7b001 correct urls && fix naming 2023-11-14 14:28:13 +01:00
serinko 2c0a561cd5 change html syntax 2023-11-14 14:20:44 +01:00
Tommy Verrall d187d252fb Merge pull request #4132 from nymtech/dependabot/npm_and_yarn/axios-1.6.0
Bump axios from 1.5.1 to 1.6.0
2023-11-14 13:04:38 +00:00
Tommy Verrall 4026dc8eef Merge pull request #4133 from nymtech/dependabot/npm_and_yarn/nym-api/tests/axios-1.6.0
Bump axios from 0.27.2 to 1.6.0 in /nym-api/tests
2023-11-14 12:59:51 +00:00
Sachin Kamath c02453b2d1 docs: update staking denoms, outdated validator info and small improvements 2023-11-14 17:21:39 +05:30
Jędrzej Stuczyński f1a5a0ccd7 returning 'nil' for non-existing origin as opposed to an empty string (#4135)
* returning 'nil' for non-existing origin as opposed to an empty string

* version bump
2023-11-14 11:16:16 +00:00
mx f9a4ca5a22 Merge pull request #4124 from nymtech/nymtech/docs/feature/updates
Update documentation
2023-11-13 18:31:42 +00:00
serinko ee99843b51 correction point ordering - PR finished 2023-11-13 18:46:56 +01:00
serinko 05e349cf37 spell check 2023-11-13 17:44:33 +01:00
serinko 8d51cd1afd spell check 2023-11-13 17:40:30 +01:00
serinko a2fd78963c spell check 2023-11-13 17:39:03 +01:00
serinko 59d43e1acd spell check 2023-11-13 17:36:33 +01:00
serinko 354c529cea syntax edit 2023-11-13 17:35:33 +01:00
serinko 435a60aee9 syntax edit 2023-11-13 17:34:55 +01:00
serinko a1c9b9b4bb syntax edit 2023-11-13 17:33:54 +01:00
serinko 457d1e8615 syntax edit 2023-11-13 17:33:21 +01:00
serinko 05eb05643f correct link path 2023-11-13 15:53:54 +01:00
serinko 3d82f84e1d correct link path 2023-11-13 15:52:16 +01:00
serinko c7b3999dcf add command example 2023-11-13 15:51:42 +01:00
serinko 8336bb0009 comment a reduntand page 2023-11-13 15:48:14 +01:00
serinko 4cb0231acf add run binary steps 2023-11-13 15:47:27 +01:00
serinko 3715860a47 add donwload binary steps 2023-11-13 15:43:38 +01:00
serinko 5b2e4158bd change version variable 2023-11-13 15:37:36 +01:00
serinko 2ddd34f343 edit syntax 2023-11-13 15:00:19 +01:00
serinko 564cbadc6e edit gateway bonding 2023-11-13 14:58:31 +01:00
serinko 5a9920edb8 simplify bonding sequence 2023-11-13 14:48:41 +01:00
serinko 0e312f66ea make node upgrade steps more explicit 2023-11-13 14:46:26 +01:00
serinko 8ca2ef28e6 edit node upgrade steps and add auto scripts 2023-11-13 13:53:32 +01:00
serinko 0cd0139307 edit Network requester to Network Requester 2023-11-13 13:02:04 +01:00
serinko 0041b4a7a7 edit Mix node to Mix Node 2023-11-13 13:00:11 +01:00
serinko caa18f1661 edit Network requester to Network Requester 2023-11-13 12:58:35 +01:00
serinko 946ced541c edit Mix node to Mix Node 2023-11-13 12:57:03 +01:00
serinko fd0c4c2623 edit Network requester to Network Requester 2023-11-13 12:55:54 +01:00
serinko 9f57ea4309 edit Mix node to Mix Node 2023-11-13 12:54:59 +01:00
serinko 4c7a30a16d edit Mix node to Mix Node 2023-11-13 12:50:35 +01:00
serinko 8029136251 edit Network requester to Network Requester 2023-11-13 12:50:10 +01:00
serinko 2a3d898da1 edit gateway to Gateway 2023-11-13 12:48:07 +01:00
serinko 0dd1f3ac2b edit Network requester to Network Requester 2023-11-13 12:47:26 +01:00
serinko 2edd704e39 edit Mix node to Mix Node 2023-11-13 12:45:54 +01:00
serinko e936ba1d26 edit Mix node to Mix Node 2023-11-13 12:39:54 +01:00
serinko d6a9f4c549 syntax edit 2023-11-13 12:34:57 +01:00
serinko aa65b96ef2 reorder Mix Node setup steps 2023-11-13 11:52:18 +01:00
serinko b5bb3f36bf add steps prior to bond 2023-11-13 11:46:08 +01:00
serinko 9f5c225cf9 edit Network requester to Network Requester 2023-11-13 11:32:16 +01:00
serinko f0864adfe6 mix node to Mix Node 2023-11-13 11:31:18 +01:00
serinko 532fea38d5 edit gateway to Gateway 2023-11-13 11:30:22 +01:00
serinko 83eb0cbf54 mix node to Mix Node 2023-11-13 11:29:35 +01:00
serinko 58e0330f4f mix node to Mix Node 2023-11-13 11:29:16 +01:00
dependabot[bot] aa8accfbf8 Bump axios from 0.27.2 to 1.6.0 in /nym-api/tests
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-11 06:19:19 +00:00
dependabot[bot] efc83bdc1a Bump axios from 1.5.1 to 1.6.0
Bumps [axios](https://github.com/axios/axios) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.5.1...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-10 17:28:32 +00:00
serinko f623a9967c unify naming convention network requester to Network requester 2023-11-09 18:13:54 +01:00
serinko 5395eebaef unify naming convention mixnode to Mix node 2023-11-09 18:13:18 +01:00
serinko 7ffe4dd1d8 unify naming convention gateway to Gateway 2023-11-09 18:12:23 +01:00
serinko 1d292d4688 unify naming convention 2023-11-09 18:09:09 +01:00
serinko dd04d4ea46 unify naming convention 2023-11-09 18:08:34 +01:00
serinko 3620cc9df0 unify naming convention gateway to Gateway 2023-11-09 18:08:00 +01:00
serinko b06d6ff412 unify naming convention gateway to Gateway 2023-11-09 17:57:51 +01:00
serinko 6088c835a3 unify naming convention mixnode to Mix node 2023-11-09 17:53:51 +01:00
serinko 9113658a42 unify naming convention 2023-11-09 17:34:00 +01:00
serinko d09503edf4 unify naming convention gateway to Gateway 2023-11-09 17:22:09 +01:00
serinko 09124dafac unify naming convention network requester to Network requester 2023-11-09 17:20:20 +01:00
serinko a2ede72798 unify naming convention network requester to Network requester 2023-11-09 17:16:55 +01:00
serinko 61296b58e9 unify naming convention mixnode to Mix node 2023-11-09 17:15:34 +01:00
serinko e3f3c5620d spell check 2023-11-09 17:06:26 +01:00
serinko c656b3968b add firo intro 2023-11-09 17:03:10 +01:00
serinko 41f9b9b340 rename gateway to Gateway 2023-11-09 14:56:15 +01:00
serinko 7d12b91bbd remove --host flag 2023-11-09 14:52:51 +01:00
serinko 2960a4c48e add --listening-address and --public-ips 2023-11-09 14:50:28 +01:00
serinko 4a84274055 rename gateway to Gateway 2023-11-09 14:43:27 +01:00
serinko c34d89165c syntax edit 2023-11-09 14:37:44 +01:00
serinko f1b0a60b34 syntax edit 2023-11-09 14:37:32 +01:00
serinko abef9c9768 add firo wallet setup 2023-11-09 14:34:28 +01:00
serinko c4b227f66e create firo setup screenshot 2023-11-09 14:31:50 +01:00
serinko 2389d7e62f add firo to SUMMARY.md 2023-11-09 14:23:40 +01:00
serinko f5e16cda5e initialise firo guide 2023-11-09 14:22:50 +01:00
serinko 7ea415c082 finish electrum guide 2023-11-09 14:21:17 +01:00
serinko 90bfeb3dd2 add electrum to SUMMARY.md 2023-11-09 14:15:25 +01:00
serinko f8666cec45 add NC install steps 2023-11-09 14:13:34 +01:00
serinko f6e5892de7 initialise electrum guide 2023-11-09 14:01:30 +01:00
340 changed files with 45695 additions and 7791 deletions
Generated
+7 -4
View File
@@ -2994,7 +2994,7 @@ dependencies = [
[[package]]
name = "extension-storage"
version = "1.2.4-rc.1"
version = "1.2.4-rc.2"
dependencies = [
"bip39",
"console_error_panic_hook",
@@ -5568,7 +5568,7 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.2.4-rc.1"
version = "1.2.4-rc.2"
dependencies = [
"async-trait",
"futures",
@@ -6270,7 +6270,7 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.2.4-rc.1"
version = "1.2.4-rc.2"
dependencies = [
"anyhow",
"futures",
@@ -6667,12 +6667,15 @@ dependencies = [
"nym-bin-common",
"nym-client-core",
"nym-config",
"nym-exit-policy",
"nym-network-requester",
"nym-sdk",
"nym-service-providers-common",
"nym-sphinx",
"nym-task",
"nym-wireguard",
"nym-wireguard-types",
"reqwest",
"serde",
"serde_json",
"tap",
@@ -6972,7 +6975,7 @@ dependencies = [
[[package]]
name = "nym-node-tester-wasm"
version = "1.2.4-rc.1"
version = "1.2.4-rc.2"
dependencies = [
"futures",
"js-sys",
@@ -4,6 +4,7 @@
use clap::{Args, Subcommand};
pub mod update_config;
pub mod update_cost_params;
pub mod vesting_update_config;
#[derive(Debug, Args)]
@@ -20,7 +21,5 @@ pub enum MixnetOperatorsMixnodeSettingsCommands {
/// Update mixnode configuration for a mixnode bonded with locked tokens
VestingUpdateConfig(vesting_update_config::Args),
/// Update mixnode cost parameters
UpdateCostParameters,
/// Update mixnode cost parameters for a mixnode bonded with locked tokens
VestingUpdateCostParameters,
UpdateCostParameters(update_cost_params::Args),
}
@@ -0,0 +1,48 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use cosmwasm_std::Uint128;
use log::info;
use nym_mixnet_contract_common::{MixNodeCostParams, Percent};
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
use nym_validator_client::nyxd::CosmWasmCoin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(
long,
help = "input your profit margin as follows; (so it would be 10, rather than 0.1)"
)]
pub profit_margin_percent: Option<u8>,
#[clap(
long,
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub interval_operating_cost: Option<u128>,
}
pub async fn update_cost_params(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
let cost_params = MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(
args.profit_margin_percent.unwrap_or(10) as u64,
)
.unwrap(),
interval_operating_cost: CosmWasmCoin {
denom: denom.into(),
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
},
};
info!("Starting mixnode params updating!");
let res = client
.update_mixnode_cost_params(cost_params, None)
.await
.expect("failed to update cost params");
info!("Cost params result: {:?}", res)
}
+10 -2
View File
@@ -25,12 +25,20 @@ pub const DEFAULT_CONFIG_FILENAME: &str = "config.toml";
#[cfg(feature = "dirs")]
pub fn must_get_home() -> PathBuf {
dirs::home_dir().expect("Failed to evaluate $HOME value")
if let Some(home_dir) = std::env::var_os("NYM_HOME_DIR") {
home_dir.into()
} else {
dirs::home_dir().expect("Failed to evaluate $HOME value")
}
}
#[cfg(feature = "dirs")]
pub fn may_get_home() -> Option<PathBuf> {
dirs::home_dir()
if let Some(home_dir) = std::env::var_os("NYM_HOME_DIR") {
Some(home_dir.into())
} else {
dirs::home_dir()
}
}
pub trait NymConfigTemplate: Serialize {
@@ -134,13 +134,15 @@ impl TunDevice {
nat_table.nat_table.insert(src_addr, tag);
}
self.tun
.write_all(&packet)
.await
.tap_err(|err| {
log::error!("iface: write error: {err}");
})
.ok();
tokio::time::timeout(
std::time::Duration::from_millis(1000),
self.tun.write_all(&packet),
)
.await
.tap_err(|err| {
log::error!("iface: write error: {err}");
})
.ok();
}
// Receive reponse packets from the wild internet
@@ -163,14 +165,25 @@ impl TunDevice {
match self.routing_mode {
// This is how wireguard does it, by consulting the AllowedIPs table.
RoutingMode::AllowedIps(ref peers_by_ip) => {
let peers = peers_by_ip.peers_by_ip.as_ref().lock().await;
let Ok(peers) = tokio::time::timeout(
std::time::Duration::from_millis(1000),
peers_by_ip.peers_by_ip.as_ref().lock(),
)
.await
else {
log::error!("Failed to lock peer");
return;
};
if let Some(peer_tx) = peers.longest_match(dst_addr).map(|(_, tx)| tx) {
log::info!("Forward packet to wg tunnel");
peer_tx
.send(Event::Ip(packet.to_vec().into()))
.await
.tap_err(|err| log::error!("{err}"))
.ok();
tokio::time::timeout(
std::time::Duration::from_millis(1000),
peer_tx.send(Event::Ip(packet.to_vec().into())),
)
.await
.tap_err(|err| log::error!("Failed to forward packet to wg tunnel: {err}"))
.ok();
return;
}
}
@@ -179,11 +192,13 @@ impl TunDevice {
RoutingMode::Nat(ref nat_table) => {
if let Some(tag) = nat_table.nat_table.get(&dst_addr) {
log::info!("Forward packet with tag: {tag}");
self.tun_task_response_tx
.send((*tag, packet.to_vec()))
.await
.tap_err(|err| log::error!("{err}"))
.ok();
tokio::time::timeout(
std::time::Duration::from_millis(1000),
self.tun_task_response_tx.send((*tag, packet.to_vec())),
)
.await
.tap_err(|err| log::error!("Failed to foward packet with tag: {err}"))
.ok();
return;
}
}
@@ -201,20 +216,32 @@ impl TunDevice {
len = self.tun.read(&mut buf) => match len {
Ok(len) => {
let packet = &buf[..len];
self.handle_tun_read(packet).await;
tokio::time::timeout(
std::time::Duration::from_millis(1000),
self.handle_tun_read(packet)
)
.await
.tap_err(|_err| log::error!("Failed: handle_tun_read timeout"))
.ok();
},
Err(err) => {
log::info!("iface: read error: {err}");
break;
// break;
}
},
// Writing to the TUN device
Some(data) = self.tun_task_rx.recv() => {
self.handle_tun_write(data).await;
tokio::time::timeout(
std::time::Duration::from_millis(1000),
self.handle_tun_write(data)
)
.await
.tap_err(|_err| log::error!("Failed: handle_tun_write timeout"))
.ok();
}
}
}
log::info!("TUN device shutting down");
// log::info!("TUN device shutting down");
}
pub fn start(self) {
+2
View File
@@ -21,6 +21,8 @@
- [NymConnect X Monero](tutorials/monero.md)
- [NymConnect X Matrix](tutorials/matrix.md)
- [NymConnect X Telegram](tutorials/telegram.md)
- [NymConnect X Electrum](tutorials/electrum.md)
- [NymConnect X Firo wallet](tutorials/firo.md)
# Code Examples
Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

@@ -10,3 +10,6 @@ We expect to see the following for submissions:
- If a UI-based solution:
- How to run it locally? We are happy to also accept staging deployments as part of the submission (e.g. via Vercel) but this does not replace being able to run it locally.
- Please make sure that your application works on commonly reproducible system environments (e.g. if youre developing on Artix Linux please check for the necessary dependencies for more common-place OSes such as Debian, or Arch). If you are developing on Windows please make sure that it works on non-Windows machines also. Where possible please try to include build and install instructions for a variety of OSes.
## How to submit?
Please follow the instructions [here](https://github.com/nymtech/nym/discussions/4143).
@@ -0,0 +1,36 @@
# Electrum Wallet NymConnect Integration
Electrum is one of the most favorite Bitcoin wallet for desktop users and it is used as a backend wallet for various crypto aplications in smart phones. Electrum was among the first integrations of Nym. This easy setup allows users to enhance privacy when managing the flagship of blochain cryptocurencies Bitcoin.
## How can I use Bitcoin over the Nym mixnet?
> Any syntax in `<>` brackets is a users unique variable. Exchange with a corresponding name without the `<>` brackets.
### NymConnect Installation
NymConnect application is for everyone who does not want to install and run `nym-socks5-client`. NymConnect is plug-and-play, fast and easy use. Electrum Bitcoin wallet, Monero wallet (desktop and CLI) and Matrix (Element app) connects through NymConnect automatically to the Mixnet.
1. [Download](https://nymtech.net/download/nymconnect) NymConnect
2. On Linux and Mac, make executable by opening terminal in the same directory and run:
```sh
chmod +x ./nym-connect_<VERSION>
```
3. Start the application
4. Click on `Connect` button to initialise the connection with the Mixnet
5. Anytime you'll need to setup Host and Port in your applications, click on `IP` and `Port` to copy the values to clipboard
6. In case you have problems such as `Gateway Issues`, try to reconnect or restart the application
### Electrum Bitcoin wallet via NymConnect
To download Electrum visit the [official webpage](https://electrum.org/#download). To connect to the Mixnet follow these steps:
7. Start and connect [NymConnect](./electrum.md#nymconnect-installation) (or [`nym-socks5-client`](https://nymtech.net/docs/clients/socks5-client.html))
2. Start your Electrum Bitcoin wallet
3. Go to: *Tools* -> *Network* -> *Proxy*
4. Set *Use proxy* to ✅, choose `SOCKS5` from the drop-down and add the values from your NymConnect application
5. Now your Electrum Bitcoin wallet runs through the Mixnet and it will be connected only if your NymConnect or `nym-socks5-client` are connected.
![Electrum Bitcoin wallet setup](../images/electrum_tutorial/electrum.gif)
@@ -0,0 +1,36 @@
# Firo-Electrum Wallet NymConnect Integration
[Firo](https://github.com/firoorg/firo#firo) (formerly Zcoin) is a privacy focused, zk-proof based cryptocurrency. Now users can enjoy Firo with network privacy by Nym as Firo's fork of Electrum wallet was integrated to work behind the Mixnet. Read more about Firo on their [official webpage](https://firo.org/).
## How can I use Firo over the Nym Mixnet?
> Any syntax in `<>` brackets is a users unique variable. Exchange with a corresponding name without the `<>` brackets.
### NymConnect Installation
NymConnect application is for everyone who does not want to install and run `nym-socks5-client`. NymConnect is plug-and-play, fast and easy use. Electrum Bitcoin wallet, Monero wallet (desktop and CLI) and Matrix (Element app) connects through NymConnect automatically to the Mixnet.
1. [Download](https://nymtech.net/download/nymconnect) NymConnect
2. On Linux and Mac, make executable by opening terminal in the same directory and run:
```sh
chmod +x ./nym-connect_<VERSION>
```
3. Start the application
4. Click on `Connect` button to initialise the connection with the Mixnet
5. Anytime you'll need to setup Host and Port in your applications, click on `IP` and `Port` to copy the values to clipboard
6. In case you have problems such as `Gateway Issues`, try to reconnect or restart the application
### Firo Electrum wallet via NymConnect
To download Firo Electrum wallet visit the [Firo's repository](https://github.com/firoorg/firo) or [Github release page](https://github.com/firoorg/electrum-firo/releases/tag/4.1.5.2). To connect to the Mixnet follow these steps:
7. Start and connect [NymConnect](./firo.md#nymconnect-installation) (or [`nym-socks5-client`](https://nymtech.net/docs/clients/socks5-client.html))
8. Start your Firo Electrum wallet
9. Go to: *Tools* -> *Network* -> *Proxy*
10. Set *Use proxy* to ✅, choose `SOCKS5` from the drop-down and add the values from your NymConnect application
11. Now your Firo Electrum wallet runs through the Mixnet and it will be connected only if your NymConnect or `nym-socks5-client` are connected.
![Firo Electrum wallet setup](../images/firo_tutorial/firo.png)
@@ -16,7 +16,7 @@ Heres how to configure Telegram with NymConnect:
For more releases, check out [Github](https://github.com/nymtech/nym/tags). NymConnect is available for Linux, Windows, and MacOS.
On Linux make sure NymConnect is executable. Opening a terminal in the same directory and run:
```sh
chmod +x ./<YOUR-NYM-CONNECT-VERSION>.AppImage
chmod +x ./<VERSION>
```
2. **Start NymConnect**
Telegram is added to NymConnect by default.
+4 -4
View File
@@ -1,12 +1,12 @@
# Introduction
This is Nym's technical documentation, containing information and setup guides about the various pieces of Nym software such as different mixnet infrastructure nodes, application clients, and existing applications like the desktop wallet and mixnet explorer.
This is Nym's technical documentation, containing information and setup guides about the various pieces of Nym software such as different Mixnet infrastructure nodes, application clients, and existing applications like the desktop wallet and Mixnet explorer.
If you are new to Nym and want to learn about the mixnet, explore kickstart options and demos, learn how to integrate with the network, and follow developer tutorials check out the [Developer Portal](https://nymtech.net/developers/) where you can find also our [FAQ section](https://nymtech.net/developers/faq/general-faq.md).
If you are new to Nym and want to learn about the Mixnet, explore kickstart options and demos, learn how to integrate with the network, and follow developer tutorials check out the [Developer Portal](https://nymtech.net/developers/) where you can find also our [FAQ section](https://nymtech.net/developers/faq/general-faq.html).
If you are looking for information and setup guides for the various pieces of Nym mixnet infrastructure (mix nodes, gateways and network requesters) and Nyx blockchain validators see the **new [Operators Guides](https://nymtech.net/operators)** book.
If you are looking for information and setup guides for the various pieces of Nym Mixnet infrastructure (Mix Nodes, Gateways and Network Requesters) and Nyx blockchain validators see the **new [Operators Guides](https://nymtech.net/operators)** book.
If you're specically looking for TypeScript/JavaScript related information such as SDKs to build your own tools, step-by-step tutorials, live playgrounds and more - make sure to check out the **new [TS SDK Handbook](https://sdk.nymtech.net/)** !
If you're specifically looking for TypeScript/JavaScript related information such as SDKs to build your own tools, step-by-step tutorials, live playgrounds and more - make sure to check out the **new [TS SDK Handbook](https://sdk.nymtech.net/)** !
## Popular pages
**Network Architecture:**
+1 -1
View File
@@ -5,7 +5,7 @@
# Binaries
- [Pre-built Binaries](binaries/pre-built-binaries.md)
- [Binary Initialisation and Configuration](binaries/init-and-config.md)
<!-- - [Binary Initialisation and Configuration](binaries/init-and-config.md) -->
- [Building from Source](binaries/building-nym.md)
<!-- - [Version Compatibility Table](binaries/version-compatiblity.md) -->
@@ -4,3 +4,31 @@ The [Github releases page](https://github.com/nymtech/nym/releases) has pre-buil
If the pre-built binaries don't work or are unavailable for your system, you will need to build the platform yourself.
## Setup Binaries
> Any syntax in `<>` brackets is a users unique variable. Exchange with a corresponding name without the `<>` brackets.
### Download Binary
1. Open [Github releases page](https://github.com/nymtech/nym/releases) and right click on the binary you want
2. Select `Copy Link`
3. Open your VPS terminal in a directory where you want to download Nym binaries.
4. Download binary by running `wget <BINARY_LINK>` where `<BINARY_LINK>` shall be in your clipboard from point \# 2.
### Make Executable
5. Run command:
```sh
chmod +x <BINARY>
# for example: chmod +x nym-mixnode
```
### Run Binary
Now you can use your binary, initialise and run your Nym Node. Follow the guide according to the type of your binary.
**Node setup and usage guides:**
* [Mix nodes](../nodes/mix-node-setup.md)
* [Gateways](../nodes/gateway-setup.md)
* [Network requesters](../nodes/network-requester-setup.md)
* [Validators](../nodes/validator-setup.md)
+18 -10
View File
@@ -1,20 +1,20 @@
# Frequently Asked Questions
## Mixnet nodes
## Nym Nodes
### What determines the rewards when running a mix node?
### What determines the rewards when running a Mix Node?
The stake required for a mix node to achieve maximum rewards is called mix node saturation point. This is calculated from the staking supply (all circulating supply + part of unlocked tokens). The target level of staking is to have 50% of the staking supply locked in mix nodes.
The stake required for a Mix Node to achieve maximum rewards is called Mix Node saturation point. This is calculated from the staking supply (all circulating supply + part of unlocked tokens). The target level of staking is to have 50% of the staking supply locked in Mix Nodes.
The node stake saturation point, which we denote by Nsat, is given by the stake supply, target level of staking divided by the number of rewarded (active) nodes.
This design ensures the nodes aim to have a same size of stake (reputation) which can be done by delegation staking, as well as it ensures that there is a decentralization of staking as any higher level of staked tokens per node results in worse rewards. On the contrary, the more mix nodes are active, the lower is Nsat. The equilibrium is reached when the staked tokens are delegated equally across the active mix-nodes and that's our basis for this incentive system.
This design ensures the nodes aim to have a same size of stake (reputation) which can be done by delegation staking, as well as it ensures that there is a decentralization of staking as any higher level of staked tokens per node results in worse rewards. On the contrary, the more Mix Nodes are active, the lower is Nsat. The equilibrium is reached when the staked tokens are delegated equally across the active mix-nodes and that's our basis for this incentive system.
For more detailed calculation, read our blog post [Nym Token Economics update](https://blog.nymtech.net/nym-token-economics-update-fedff0ed5267). More info on staking can be found [here](https://blog.nymtech.net/staking-in-nym-introducing-mainnet-mixmining-f9bb1cbc7c36). And [here](https://blog.nymtech.net/want-to-stake-in-nym-here-is-how-to-choose-a-mix-node-to-delegate-nym-to-c3b862add165) is more info on how to choose a mix node for delegation. And finally an [update](https://blog.nymtech.net/quarterly-token-economic-parameter-update-b2862948710f) on token economics from July 2023.
For more detailed calculation, read our blog post [Nym Token Economics update](https://blog.nymtech.net/nym-token-economics-update-fedff0ed5267). More info on staking can be found [here](https://blog.nymtech.net/staking-in-nym-introducing-mainnet-mixmining-f9bb1cbc7c36). And [here](https://blog.nymtech.net/want-to-stake-in-nym-here-is-how-to-choose-a-mix-node-to-delegate-nym-to-c3b862add165) is more info on how to choose a Mix Node for delegation. And finally an [update](https://blog.nymtech.net/quarterly-token-economic-parameter-update-b2862948710f) on token economics from July 2023.
### Which VPS providers would you recommend?
Consider in which jurisdiction you reside and where do you want to run a mix node. Do you want to pay by crypto or not and what are the other important particularities for your case? We always recommend operators to try to choose smaller and decentralised VPS providers over the most known ones controlling a majority of the internet. We receive some good feedback on these: Linode, Ghandi, Flokinet and Exoscale. Do your own research and share with the community.
Consider in which jurisdiction you reside and where do you want to run a Mix Node. Do you want to pay by crypto or not and what are the other important particularities for your case? We always recommend operators to try to choose smaller and decentralised VPS providers over the most known ones controlling a majority of the internet. We receive some good feedback on these: Linode, Ghandi, Flokinet and Exoscale. Do your own research and share with the community.
<!---### Why is a mix node setup on a self-hosted machine so tricky?--->
@@ -22,17 +22,17 @@ Consider in which jurisdiction you reside and where do you want to run a mix nod
The sizes are shown in the configs [here](https://github.com/nymtech/nym/blob/1ba6444e722e7757f1175a296bed6e31e25b8db8/common/nymsphinx/params/src/packet_sizes.rs#L12) (default is the one clients use, the others are for research purposes, not to be used in production as this would fragment the anonymity set). More info can be found [here](https://github.com/nymtech/nym/blob/4844ac953a12b29fa27688609ec193f1d560c996/common/nymsphinx/anonymous-replies/src/reply_surb.rs#L80).
### Why a mix node and a gateway cannot be bond to the same wallet?
### Why a Mix Node and a Gateway cannot be bonded with the same wallet?
Because of the way the smart contract works we keep it one-node one-address at the moment.
### Which nodes are the most needed to be setup to strengthen Nym infrastructure and which ones bring rewards?
Right now only mix nodes are rewarded. We're working on gateway and service payments. Gateways are the weak link right now due mostly to lack of incentivisation. Services like Network Requesters are obviously the most necessary for people to start using the platform, and we're working on smart contracts to allow for people to start advertising them the same way they do mix nodes.
Right now only Mix Nodes are rewarded. We're working on Gateway and service payments. Gateways are the weak link right now due mostly to lack of incentivisation. Services like Network Requesters are obviously the most necessary for people to start using the platform, and we're working on smart contracts to allow for people to start advertising them the same way they do Mix Nodes.
### Are mixnodes whitelisted?
### Are Mix Nodes whitelisted?
Nope, anyone can run a mix node. Purely reliant on the node's reputation (self stake + delegations) & routing score.
Nope, anyone can run a Mix Node. Purely reliant on the node's reputation (self stake + delegations) & routing score.
## Validators and tokens
@@ -44,6 +44,14 @@ Nope, anyone can run a mix node. Purely reliant on the node's reputation (self s
### What's the difference between NYM and NYX?
--->
### Why some Nyx blockchain operations take one hour and others are instant?
This is based on the definition in [Nym's CosmWasm](https://github.com/nymtech/nym/tree/develop/common/cosmwasm-smart-contracts) smart contracts code.
Whatever is defined as [a pending epoch event](https://github.com/nymtech/nym/blob/b07627d57e075b6de35b4b1a84927578c3172811/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs#L35-L103) will get resolved at the end of the current epoch.
And whatever is defined as [a pending interval event](https://github.com/nymtech/nym/blob/b07627d57e075b6de35b4b1a84927578c3172811/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs#L145-L172) will get resolved at the end of the current interval.
### Can I run a validator?
We are currently working towards building up a closed set of reputable validators. You can ask us for coins to get in, but please don't be offended if we say no - validators are part of our system's core security and we are starting out with people we already know or who have a solid reputation.
+32 -26
View File
@@ -4,7 +4,8 @@
> -- Harry Halpin, Nym CEO
<br>
This page refer to the changes which are planned to take place over Q3 and Q4 2023. As this is a transition period in the beginning (Q3 2023) the [Mix Nodes FAQ page](./mixnodes-faq.md) holds more answers to the current setup as project Smoosh refers to the eventual setup. As project Smoosh gets progressively implemented the answers on this page will become to be more relevant to the current state and eventually this FAQ page will be merged with the still relevant parts of the main Mix Nodes FAQ page.
This page refer to the changes which are planned to take place over Q3 and Q4 2023. As this is a transition period in the beginning (Q3 2023) the [Mix Nodes FAQ page](mixnodes-faq.md) holds more answers to the current setup as project Smoosh refers to the eventual setup. As project Smoosh gets progressively implemented the answers on this page will become to be more relevant to the current state and eventually this FAQ page will be merged with the still relevant parts of the main Mix Nodes FAQ page.
If any questions are not answered or it's not clear for you in which stage project Smoosh is right now, please reach out in Node Operators [Matrix room](https://matrix.to/#/#operators:nymtech.chat).
@@ -14,33 +15,36 @@ If any questions are not answered or it's not clear for you in which stage proje
As we shared in our blog post article [*What does it take to build the wolds most powerful VPN*](https://blog.nymtech.net/what-does-it-take-to-build-the-worlds-most-powerful-vpn-d351a76ec4e6), project Smoosh is:
> A nick-name by CTO Dave Hrycyszyn and Chief Scientist Claudia Diaz for the work they are currently doing to “smoosh” Nym nodes so that the same operator can serve alternately as mix node, gateway or VPN node. This requires careful calibration of the Nym token economics, for example, only nodes with the highest reputation for good quality service will be in the VPN set and have the chance to earn higher rewards.
> A nick-name by CTO Dave Hrycyszyn and Chief Scientist Claudia Diaz for the work they are currently doing to “smoosh” Nym Nodes so that the same operator can serve alternately as Mix Node, Gateway or VPN node. This requires careful calibration of the Nym token economics, for example, only nodes with the highest reputation for good quality service will be in the VPN set and have the chance to earn higher rewards.
> By simplifying the components, adding VPN features and supporting new node operators, the aim is to widen the geographical coverage of nodes and have significant redundancy, meaning plenty of operators to be able to meet demand. This requires strong token economic incentives as well as training and support for new node operators.
## Technical Questions
### What are the changes?
Project smoosh will have three steps:
Project Smoosh will have four steps, please follow the table below to track the dynamic progress:
1. Combine the `gateway` and `network-requester` into one binary ✅
2. Create [Exit Gateway](../legal/exit-gateway.md): Take the gateway binary including network requester combined in \#1 and switch from [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) ✅
3. Combine all the nodes in the Nym Mixnet into one binary, that is `mixnode`, `gateway` (entry and exit) and `network-requester`.
| **Step** | **Status** |
| :--- | :--- |
| **1.** Combine the `nym-gateway` and `nym-network-requester` into one binary | ✅ done |
| **2.** Create [Exit Gateway](../legal/exit-gateway.md): Take the `nym-gateway` binary including `nym-network-requester` combined in \#1 and switch from [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) | ✅ done |
| **3.** Combine all the nodes in the Nym Mixnet into one binary, that is `nym-mixnode`, `nym-gateway` (entry and exit) and `nym-network-requester`. | 🛠️ in progress |
| **4.** Adjust reward scheme to incentivise and reward Exit Gateways as a part of `nym-node` binary, implementing [zkNym credentials](https://youtu.be/nLmdsZ1BsQg?t=1717). | 🛠️ in progress |
These three steps will be staggered over time - period of several months, and will be implemented one by one with enough time to take in feedback and fix bugs in between.
Generally, the software will be the same, just instead of multiple binaries, there will be one Nym Mixnet node binary. Delegations will remain on as they are now, per our token economics (staking, saturation etc)
These steps will be staggered over time - period of several months, and will be implemented one by one with enough time to take in feedback and fix bugs in between.
Generally, the software will be the same, just instead of multiple binaries, there will be one Nym Node (`nym-node`) binary. Delegations will remain on as they are now, per our token economics (staking, saturation etc)
### What does it mean for Nym nodes operators?
We are exploring two potential methods for implementing binary functionality in practice and will provide information in advance. The options are:
1. Make a selection button (command/argument/flag) for operators to choose whether they want their node to provide all or just some of the functions nodes have in the Nym Mixnet. Nodes functioning as exit gateways (in that epoch) will then have bigger rewards due to their larger risk exposure and overhead work with the setup.
1. Make a selection button (command/argument/flag) for operators to choose whether they want their node to provide all or just some of the functions nodes have in the Nym Mixnet. Nodes functioning as Exit Gateways (in that epoch) will then have bigger rewards due to their larger risk exposure and overhead work with the setup.
2. All nodes will be required to have the exit gateway functionality. All nodes are rewarded the same as now, and the difference is that a node sometimes (some epochs) may be performing as exit gateway sometimes as mix node or entry gateway adjusted according the network demand by an algorithm.
2. All nodes will be required to have the Exit Gateway functionality. All nodes are rewarded the same as now, and the difference is that a node sometimes (some epochs) may be performing as Exit Gateway sometimes as Mix node or Entry Gateway adjusted according the network demand by an algorithm.
### Where can I read more about the exit gateway setup?
### Where can I read more about the Exit Gateway setup?
We created an [entire page](../legal/exit-gateway.md) about the technical and legal questions around exit gateway.
We created an [entire page](../legal/exit-gateway.md) about the technical and legal questions around Exit Gateway.
### What is the change from allow list to deny list?
@@ -48,15 +52,17 @@ The operators running Gateways would have to “open” their nodes to a wider r
### How will the Exit policy be implemented?
The progression of exit policy on Gateways will have three steps:
Follow the dynamic progress of exit policy implementation on Gateways below:
1. By default the [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) filtering will be disabled and the current [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) filtering is going to continue be used. This is to prevent operators getting surprised by upgrading their Gateways (or Network requesters) and suddenly be widely open to the internet. To enable the new exit policy, operators must use `--with-exit-policy` flag or modify the `config.toml` file. ✅
2. Relatively soon the exit policy will be part of the Gateway setup by default. To disable this exit policy, operators must use `--disable-exit-policy` flag.
3. Further down the line, it will be the only option. Then the `allowed.list` will be completely removed.
| **Step** | **Status** |
| :--- | :--- |
| **1.** By default the [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) filtering is disabled and the [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) filtering is going to continue be used. This is to prevent operators getting surprised by upgrading their Gateways (or Network Requesters) and suddenly be widely open to the internet. To enable the new exit policy, operators must use `--with-exit-policy` flag or modify the `config.toml` file. | ✅ done |
| **2.** The exit policy is part of the Gateway setup by default. To disable this exit policy, operators must use `--disable-exit-policy` flag. | 🛠️ in progress |
| **3.** The exit policy is the only option. The `allowed.list` is completely removed. | 🛠️ in progress |
Keep in mind this only relates to changes happening on Gateway and Network Requester side. Whether this will be optional or mandatory depends on the chosen [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators).
Keep in mind the table above only relates to changes happening on Gateways. For the Project Smoosh progress refer to the [table above](./smoosh-faq.md#what-are-the-changes). Whether Exit Gateway functionality will be optional or mandatory part of every active Nym Node depends on the chosen [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators).
### Can I run a mix node only?
### Can I run a Mix Node only?
It depends which [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators) will ultimately be used. In case of the first - yes. In case of the second option, all the nodes will be setup with Exit Gateway functionality turned on.
@@ -68,7 +74,7 @@ For any specifics on Nym token economics and Nym Mixnet reward system, please re
### What are the incentives for the node operator?
In the original setup there were no incentives to run a `network-requester`. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic ([`Nym API`](https://github.com/nymtech/nym/tree/master/nym-api) will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
In the original setup there were no incentives to run a `nym-network-requester` binary. After the transition all the users will buy multiple tickets of zkNyms credentials and use those as [anonymous e-cash](https://arxiv.org/abs/2303.08221) to pay for their data traffic ([`Nym API`](https://github.com/nymtech/nym/tree/master/nym-api) will do the do cryptographical checks to prevent double-spending). All collected fees get distributed to all active nodes proportionally to their work by the end of each epoch.
### How does this change the token economics?
@@ -78,9 +84,9 @@ The token economics will stay the same as they are, same goes for the reward alg
This depends on [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators) chosen. In case of \#1, it will look like this:
As each operator can choose what roles their nodes provide, the nodes which work as open gateways will have higher rewards because they are the most important to keep up and stable. Besides that the operators of gateways may be exposed to more complication and possible legal risks.
As each operator can choose what roles their nodes provide, the nodes which work as open Gateways will have higher rewards because they are the most important to keep up and stable. Besides that the operators of Gateways may be exposed to more complication and possible legal risks.
The nodes which are initialized to run as mix nodes and gateways will be chosen to be on top of the active set before the ones working only as a mix node.
The nodes which are initialized to run as Mix Nodes and Gateways will be chosen to be on top of the active set before the ones working only as a Mix Node.
I case we go with \#2, all nodes active in the epoch will be rewarded proportionally according their work.
@@ -88,21 +94,21 @@ In either way, Nym will share all the specifics beforehand.
### How will be the staking and inflation after project Smoosh?
Nym will run tests to count how much payment comes from the users of the Mixnet and if that covers the reward payments. If not, we may need to keep inflation on to secure incentives for high quality gateways in the early stage of the transition.
Nym will run tests to count how much payment comes from the users of the Mixnet and if that covers the reward payments. If not, we may need to keep inflation on to secure incentives for high quality Gateways in the early stage of the transition.
### When project smooth will be launched, it would be the mixmining pool that will pay for the gateway rewards based on amount of traffic routed ?
### When project smooth will be launched, it would be the mixmining pool that will pay for the Gateway rewards based on amount of traffic routed ?
Yes, the same pool. Nym's aim is to do minimal modifications. The only real modification on the smart contract side will be to get into top X of 'active set' operators will need to have open gateway function enabled.
Yes, the same pool. Nym's aim is to do minimal modifications. The only real modification on the smart contract side will be to get into top X of 'active set' operators will need to have open Gateway function enabled.
### What does this mean for the current delegators?
From an operator standpoint, it shall just be a standard Nym upgrade, a new option to run the gateway software on your node. Delegators should not have to re-delegate.
From an operator standpoint, it shall just be a standard Nym upgrade, a new option to run the Gateway software on your node. Delegators should not have to re-delegate.
## Legal Questions
### Are there any legal concerns for the operators?
So far the general line is that running a gateway is not illegal (unless you are in Iran, China, and a few other places) and due to encryption/mixing less risky than running a normal VPN node. For mix nodes, it's very safe as they have "no idea" what packets they are mixing.
So far the general line is that running a Gateway is not illegal (unless you are in Iran, China, and a few other places) and due to encryption/mixing less risky than running a normal VPN node. For Mix Nodes, it's very safe as they have "no idea" what packets they are mixing.
There are several legal questions and analysis to be made for different jurisdictions. To be able to share resources and findings between the operators themselves we created a [Community Legal Forum](../legal/exit-gateway.md).
+10 -11
View File
@@ -1,6 +1,6 @@
# Introduction
This is Nym's Operators guide, containing information and setup guides for the various pieces of Nym mixnet infrastructure (mix node, gateway and network requester) and Nyx blockchain validators.
This is Nym's Operators guide, containing information and setup guides for the various pieces of Nym Mixnet infrastructure (Mix Node, Gateway and Network Requester) and Nyx blockchain validators.
If you are new to Nym and want to learn about the mixnet, explore kickstart options and demos, learn how to integrate with the network, and follow developer tutorials check out the [Developer Portal](https://nymtech.net/developers/).
@@ -9,18 +9,17 @@ If you want to dive deeper into Nym's architecture, clients, nodes, and SDK exam
## Popular pages
**Binary Information**
* [Building Nym](./binaries/building-nym.md)
* [Pre-built Binaries](./binaries/pre-built-binaries.md)
* [Init & Configuration](./binaries/init-and-config.md)
* [Building Nym](binaries/building-nym.md)
* [Pre-built Binaries](binaries/pre-built-binaries.md)
**Node setup and usage guides:**
* [Mix nodes](./nodes/mix-node-setup.md)
* [Gateways](./nodes/gateway-setup.md)
* [Network requesters](./nodes/network-requester-setup.md)
* [Validators](./nodes/validator-setup.md)
* [Mix nodes](nodes/mix-node-setup.md)
* [Gateways](nodes/gateway-setup.md)
* [Network requesters](nodes/network-requester-setup.md)
* [Validators](nodes/validator-setup.md)
**Maintenance, troubleshooting and FAQ**
* [Maintenance](./nodes/maintenance.md)
* [Troubleshooting](./nodes/troubleshooting.md)
* [FAQ](./faq/mixnodes-faq.md)
* [Maintenance](nodes/maintenance.md)
* [Troubleshooting](nodes/troubleshooting.md)
* [FAQ](faq/mixnodes-faq.md)
@@ -8,7 +8,7 @@ This page is a part of Nym Community Legal Forum and its content is composed by
This document presents an initiative to further support Nyms mission of allowing privacy for everyone everywhere. This would be achieved with the support of Nym node operators operating Gateways and opening these to any online service. Such setup needs a **clear policy**, one which will remain the **same for all operators** running Nym nodes. The [proposed **Exit policy**](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) is a combination of two existing safeguards: [Tor Null deny list](https://tornull.org/) and [Tor reduced policy](https://tornull.org/tor-reduced-reduced-exit-policy.php).
All the technical changes on the side of Nym nodes - ***Project Smoosh** - are described in the [FAQ section](../faq/smoosh-faq.md).
All the technical changes on the side of Nym nodes - ***Project Smoosh*** - are described in the [FAQ section](../faq/smoosh-faq.md).
```admonish warning
Nym core team cannot provide comprehensive legal advice across all jurisdictions. Knowledge and experience with the legalities are being built up with the help of our counsel and with you, the community of Nym node operators. We encourage Nym node operators to join the operator channels ([Element](https://matrix.to/#/#operators:nymtech.chat), [Discord](https://discord.com/invite/nym), [Telegram](https://t.me/nymchan_help_chat)) to share best practices and experiences.
@@ -17,13 +17,13 @@ Nym core team cannot provide comprehensive legal advice across all jurisdictions
## Summary
* This document outlines a plan to change Nym Gateways from operating with an allow to a deny list to enable broader uptake and usage of the Nym mixnet. It provides operators with an overview of the plan, pros and cons, legal as well as technical advice.
* This document outlines a plan to change Nym Gateways from operating with an allow to a deny list to enable broader uptake and usage of the Nym Mixnet. It provides operators with an overview of the plan, pros and cons, legal as well as technical advice.
* Nym is committed to ensuring privacy for all users, regardless of their location and for the broadest possible range of online services. In order to achieve this aim, the Nym mixnet needs to increase its usability across a broad range of apps and services.
* Nym is committed to ensuring privacy for all users, regardless of their location and for the broadest possible range of online services. In order to achieve this aim, the Nym Mixnet needs to increase its usability across a broad range of apps and services.
* Currently, Nym Gateway nodes only enable access to apps and services that are on an allow list that is maintained by the core team.
* To decentralise and enable privacy for a broader range of services, this initiative will have to transition from the current allow list to a deny list - [Exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt). In accordance with the [Tor Null 'deny' list](https://tornull.org/) and [Tor reduced policy](https://tornull.org/tor-reduced-reduced-exit-policy.php), which are two established safeguards.
* To decentralise and enable privacy for a broader range of services, this initiative will have to transition from the current allow list to a deny list - [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt). In accordance with the [Tor Null 'deny' list](https://tornull.org/) and [Tor reduced policy](https://tornull.org/tor-reduced-reduced-exit-policy.php), which are two established safeguards.
* This will enhance the usage and appeal of Nym products for end users. As a result, increased usage will ultimately lead to higher revenues for Nym operators.
@@ -40,7 +40,7 @@ Nym core team cannot provide comprehensive legal advice across all jurisdictions
To offer a better and more private everyday experience for its users, Nym would like them to use any online services they please, without limiting its access to a few messaging apps or crypto wallets.
To achieve this, operators running exit gateways would have to “open” their nodes to a wider range of online services, in a similar fashion to Tor exit relays following this [Exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt).
To achieve this, operators running Exit Gateways would have to “open” their nodes to a wider range of online services, in a similar fashion to Tor exit relays following this [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt).
## Pros and cons of the initiative
@@ -64,23 +64,23 @@ The new setup: Running nodes supporting traffic of any online service (with safe
| Operational | | - Higher operational overhead, such as dealing with DMCA / abuse complaints, managing the VPS provider questions, or helping the community to maintain the denylist <br>- Administrative overhead if running nodes as a company or an entity |
| Legal | | - Ideally requires to check legal environment with local privacy association or lawyer | Financial | - Higher revenue potential for operators due to the increase in network usage | - If not running VPS with an unlimited bandwidth plan, higher costs due to higher network usage |
## Exit gateways: New setup
## Exit Gateways: New setup
In our previous technical setup, network requesters acted as a proxy, and only made requests that match an allow list. That was a default IP based list of allowed domains stored at Nym page in a centralised fashion possibly re-defined by any Network requester operator.
In our previous technical setup, Network Requesters acted as a proxy, and only made requests that match an allow list. That was a default IP based list of allowed domains stored at Nym page in a centralised fashion possibly re-defined by any Network Requester operator.
This restricts the hosts that the NymConnect app can connect to and has the effect of selectively supporting messaging services (e.g. Telegram, Matrix) or crypto wallets (e.g. Electrum or Monero). Operators of network requesters can have confidence that the infrastructure they run only connects to a limited set of public internet hosts.
This restricts the hosts that the NymConnect app can connect to and has the effect of selectively supporting messaging services (e.g. Telegram, Matrix) or crypto wallets (e.g. Electrum or Monero). Operators of Network Requesters can have confidence that the infrastructure they run only connects to a limited set of public internet hosts.
The principal change in the new configuration is to make this short allow list more permissive. Nym's [Exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) will restrict the hosts to which Nym Mixnet and Nym VPN users are permitted to connect. This will be done in an effort to protect the operators, as Gateways will act both as SOCKS5 Network Requesters, and exit nodes for IP traffic from Nym Mixnet VPN and VPN clients (both wrapped in the same app).
The principal change in the new configuration is to make this short allow list more permissive. Nym's [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) will restrict the hosts to which Nym Mixnet and Nym VPN users are permitted to connect. This will be done in an effort to protect the operators, as Gateways will act both as SOCKS5 Network Requesters, and exit nodes for IP traffic from Nym Mixnet VPN and VPN clients (both wrapped in the same app).
As of now we the gateways will be defaulted to a combination of [Tor Null deny list](https://tornull.org/) (note: Not affiliated with Tor) - reproduction permitted under Creative Commons Attribution 3.0 United States License which is IP-based, e.g., `ExitPolicy reject 5.188.10.0/23:*` and [Tor reduced policy](https://tornull.org/tor-reduced-reduced-exit-policy.php). Whether we will stick with this list, do modifications or compile another one is still a subject of discussion. In all cases, this policy will remain the same for all the nodes, without any option to modify it by Nym node operators to secure stable and reliable service for the end users.
As of now we the Gateways will be defaulted to a combination of [Tor Null deny list](https://tornull.org/) (note: Not affiliated with Tor) - reproduction permitted under Creative Commons Attribution 3.0 United States License which is IP-based, e.g., `ExitPolicy reject 5.188.10.0/23:*` and [Tor reduced policy](https://tornull.org/tor-reduced-reduced-exit-policy.php). Whether we will stick with this list, do modifications or compile another one is still a subject of discussion. In all cases, this policy will remain the same for all the nodes, without any option to modify it by Nym node operators to secure stable and reliable service for the end users.
For exit relays on ports 80 and 443, the gateways will exhibit an HTML page resembling the one proposed by [Tor](https://gitlab.torproject.org/tpo/core/tor/-/raw/HEAD/contrib/operator-tools/tor-exit-notice.html). By doing so, the operator will be able to disclose details regarding their gateway, including the currently configured exit policy, all without the need for direct correspondence with regulatory or law enforcement agencies. It also makes the behaviour of exit gateways transparent and even computable (a possible feature would be to offer a machine readable form of the notice in JSON or YAML).
For exit relays on ports 80 and 443, the Gateways will exhibit an HTML page resembling the one proposed by [Tor](https://gitlab.torproject.org/tpo/core/tor/-/raw/HEAD/contrib/operator-tools/tor-exit-notice.html). By doing so, the operator will be able to disclose details regarding their Gateway, including the currently configured exit policy, all without the need for direct correspondence with regulatory or law enforcement agencies. It also makes the behavior of Exit Gateways transparent and even computable (a possible feature would be to offer a machine readable form of the notice in JSON or YAML).
We also recommend operators to check the technical advice from [Tor](https://community.torproject.org/relay/setup/exit/).
## Tor legal advice
Giving the legal similarity between Nym exit gateways and Tor exit relays, it is helpful to have a look in [Tor community Exit Guidelines](https://community.torproject.org/relay/community-resources/tor-exit-guidelines/). This chapter is an exert of tor page.
Giving the legal similarity between Nym Exit Gateways and Tor Exit Relays, it is helpful to have a look in [Tor community Exit Guidelines](https://community.torproject.org/relay/community-resources/tor-exit-guidelines/). This chapter is an exert of tor page.
Note that Tor states:
> This FAQ is for informational purposes only and does not constitute legal advice.
@@ -88,7 +88,7 @@ Note that Tor states:
*Check legal advice prior to running an exit relay*
* Understand the risks associated with running an exit relay; E.g., know legal paragraphs relevant in the country of operations:
- US [DMCA 512](https://www.law.cornell.edu/uscode/text/17/512); see [EFF's Legal FAQ for TOr Operators](https://community.torproject.org/relay/community-resources/eff-tor-legal-faq) (a very good and relevant read for other countries as well)
- US [DMCA 512](https://www.law.cornell.edu/uscode/text/17/512); see [EFF's Legal FAQ for Tor Operators](https://community.torproject.org/relay/community-resources/eff-tor-legal-faq) (a very good and relevant read for other countries as well)
- Germanys [TeleMedienGesetz 8](http://www.gesetze-im-internet.de/tmg/__8.html) and [15](http://www.gesetze-im-internet.de/tmg/__15.html)
- Netherlands: [Artikel 6:196c BW](http://wetten.overheid.nl/BWBR0005289/Boek6/Titel3/Afdeling4A/Artikel196c/)
- Austria: [E-Commerce-Gesetz 13](http://www.ris.bka.gv.at/Dokument.wxe?Abfrage=Bundesnormen&Dokumentnummer=NOR40025809)
@@ -96,7 +96,7 @@ Note that Tor states:
* Top 3 advice
- Have an abuse response letter
- Run relay from a location that is not home
- Read through the legal resources that Tor-supportive lawyers put together: https://www.eff.org/pages/legal-faq-tor-relay-operators or https://www.noisebridge.net/wiki/Noisebridge_Tor/FBI
- Read through the legal resources that Tor-supportive lawyers put together: [www.eff.org/pages/legal-faq-tor-relay-operators](https://www.eff.org/pages/legal-faq-tor-relay-operators) or [www.noisebridge.net/wiki/Noisebridge_Tor/FBI](https://www.noisebridge.net/wiki/Noisebridge_Tor/FBI)
* Consult a lawyer / local digital rights association / the EFF prior to operating an exit relay, especially in a place where exit relay operators have been harassed or not operating before. Note that Tor DOES NOT provide legal advice for specific countries. It only provides general advice (itself or in partnership), eventually skewed towards [US audiences](https://www.eff.org/pages/legal-faq-tor-relay-operators).
@@ -16,7 +16,7 @@ As a result of [Project Smoosh](../faq/smoosh-faq.md), the current version of `n
## Preliminary steps
Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your gateway.
Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your Gateway.
## Gateway setup
@@ -50,7 +50,7 @@ You can also check the various arguments required for individual commands with:
## Initialising your Gateway
As Nym developers build towards [Exit Gateway](../legal/exit-gateway.md) functionality, operators can now run their `nym-gateway` binary with in-build Network requester and include the our new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt). Considering the plan to [*smoosh*](../faq/smoosh-faq.md) all the nodes into one binary and have wide opened Exit Gateways, we recommend this setup, instead of operating two separate binaries.
As Nym developers build towards [Exit Gateway](../legal/exit-gateway.md) functionality, operators can now run their `nym-gateway` binary with inbuilt Network Requester and include the our new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt). Considering the plan to [*smoosh*](../faq/smoosh-faq.md) all the nodes into one binary and have wide opened Exit Gateways, we recommend this setup, instead of operating two separate binaries.
```admonish warning
Before you start an Exit Gateway, read our [Operators Legal Forum](../legal/exit-gateway.md) page and [*Project Smoosh FAQ*](../faq/smoosh-faq.md).
@@ -60,19 +60,21 @@ Before you start an Exit Gateway, read our [Operators Legal Forum](../legal/exit
There has been an ongoing development with dynamic upgrades. Follow the status of the Project Smoosh [changes](../faq/smoosh-faq.md#what-are-the-changes) and the progression state of exit policy [implementation](../faq/smoosh-faq.html#how-will-the-exit-policy-be-implemented) to be up to date with the current design.
```
**Note:** Due to the development towards Exit Gateway functionality the `--host` flag has been replaced with `--listening-address`, this is the IP address which is used for receiving sphinx packets and listening to client data. Another flag `--public-ips` is required; its a comma separated list of IPs that are announced to the `nym-api`, it is usually the address which is used for bonding.
### Initialising Exit Gateway
An operator can initialise the Exit Gateway functionality by adding Network requester with the new exit policy option:
An operator can initialise the Exit Gateway functionality by adding Network Requester with the new exit policy option:
```
./nym-gateway init --id <ID> --host $(curl -4 https://ifconfig.me) --with-network-requester --with-exit-policy true
./nym-gateway init --id <ID> --listening-address 0.0.0.0 --public-ips "$(curl -4 https://ifconfig.me)" --with-network-requester --with-exit-policy true
```
If we follow the previous example with `<ID>` chosen `superexitgateway`, adding the `--with-network-requester` and `--with-exit-policy` flags, the outcome will be:
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../target/release/nym-gateway init --id superexitgateway --host $(curl -4 https://ifconfig.me) --with-network-requester --with-exit-policy true -->
<!-- cmdrun ../../../../target/release/nym-gateway init --id superexitgateway --listening-address 0.0.0.0 --public-ips "$(curl -4 https://ifconfig.me)" --with-network-requester --with-exit-policy true -->
```
~~~
@@ -80,9 +82,9 @@ You can see that the printed information besides *identity* and *sphinx keys* al
Additionally
#### Add Network requester to an existing Gateway
#### Add Network Requester to an existing Gateway
If you already [upgraded](./maintenance.md#upgrading-your-node) your Gateway to the [latest version](./gateway-setup.md#current-version) and initialised without a Network requester, you can easily change its functionality to Exit Gateway with a command `setup-network-requester`.
If you already [upgraded](./maintenance.md#upgrading-your-node) your Gateway to the [latest version](./gateway-setup.md#current-version) and initialised without a Network Requester, you can easily change its functionality to Exit Gateway with a command `setup-network-requester`.
See the options:
@@ -102,7 +104,7 @@ To setup Exit Gateway functionality with our new [exit policy](https://nymtech.n
./nym-gateway setup-network-requester --enabled true --with-exit-policy true --id <ID>
```
Say we have a gateway with `<ID>` as `new-gateway`, originally initialised and ran without the Exit Gateway functionality. To change the setup, run:
Say we have a Gateway with `<ID>` as `new-gateway`, originally initialised and ran without the Exit Gateway functionality. To change the setup, run:
```
@@ -112,7 +114,7 @@ Say we have a gateway with `<ID>` as `new-gateway`, originally initialised and r
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun rm -rf $HOME/.nym/gateways/new-gateway -->
<!-- cmdrun ../../../../target/release/nym-gateway init --id new-gateway --host $(curl -4 https://ifconfig.me) && ../../../../target/release/nym-gateway setup-network-requester --enabled true --with-exit-policy true --id new-gateway -->
<!-- cmdrun ../../../../target/release/nym-gateway init --id new-gateway --listening-address 0.0.0.0 --public-ips "$(curl -4 https://ifconfig.me)" && ../../../../target/release/nym-gateway setup-network-requester --enabled true --with-exit-policy true --id new-gateway -->
```
~~~
@@ -120,13 +122,13 @@ In case there are any unexpected problems, you can also change it manually by ed
```
[network_requester]
# Specifies whether network requester service is enabled in this process.
# Specifies whether Network Requester service is enabled in this process.
enabled = true
```
Save, exit and restart your gateway. Now you are an operator of post-smooshed Exit gateway.
Save, exit and restart your Gateway. Now you are an operator of post-smooshed Exit Gateway.
#### Enable Nym exit policy to an existing Gateway with Network requester functionality
#### Enable Nym exit policy to an existing Gateway with Network Requester functionality
In case you already added Network Requester functionality to your Gateway as described above but haven't enabled the [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) there is an easy tweak to do so and turn your node into [Nym Exit Gateway](../faq/smoosh-faq.md#what-are-the-changes).
@@ -134,18 +136,18 @@ Open the config file stored at `.nym/gateways/<ID>/config/network_requester_conf
```sh
use_deprecated_allow_list = false
```
Save, exit and restart your gateway. Now you are an operator of post-smooshed Exit gateway.
Save, exit and restart your Gateway. Now you are an operator of post-smooshed Exit gateway.
```admonish info
All information about network requester part of your Exit Gateway is in `/home/user/.nym/gateways/<ID>/config/network_requester_config.toml`.
All information about Network Requester part of your Exit Gateway is in `/home/user/.nym/gateways/<ID>/config/network_requester_config.toml`.
```
For now you can run Gateway without Network requester or with and without the new exit policy. This will soon change as we inform in our [Project Smoosh FAQ](../faq/smoosh-faq.html#how-will-the-exit-policy-be-implemented).
For now you can run Gateway without Network Requester or with and without the new exit policy. This will soon change as we inform in our [Project Smoosh FAQ](../faq/smoosh-faq.html#how-will-the-exit-policy-be-implemented).
To read more about the configuration like whitelisted outbound requesters in `allowed.list` and other useful information, see the page [*Network requester whitelist*](network-requester-setup.md#using-your-network-requester).
To read more about the configuration like whitelisted outbound requesters in `allowed.list` and other useful information, see the page [*Network Requester whitelist*](network-requester-setup.md#using-your-network-requester).
#### Initialising Gateway without Network requester
#### Initialising Gateway without Network Requester
In case you don't want to run your Gateway with the Exit Gateway functionality, you still can run a simple Gateway.
@@ -161,42 +163,50 @@ To check available configuration options use:
```
~~~
The following command returns a gateway on your current IP with the `<ID>` of `simple-gateway`:
The following command returns a Gateway on your current IP with the `<ID>` of `simple-gateway`:
```
./nym-gateway init --id simple-gateway --host $(curl -4 https://ifconfig.me)
./nym-gateway init --id simple-gateway --listening-address 0.0.0.0 --public-ips "$(curl -4 https://ifconfig.me)"
```
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../target/release/nym-gateway init --id simple-gateway --host $(curl -4 https://ifconfig.me) -->
<!-- cmdrun ../../../../target/release/nym-gateway init --id simple-gateway --listening-address 0.0.0.0 --public-ips "$(curl -4 https://ifconfig.me)" -->
```
~~~
The `$(curl -4 https://ifconfig.me)` command above returns your IP automatically using an external service. Alternatively, you can enter your IP manually if you wish. If you do this, remember to enter your IP **without** any port information.
## Running your Gateway
### Bonding your gateway
The `run` command starts the Gateway:
```admonish info
Before you bond and re-run your Gateway, please make sure the [firewall configuration](./maintenance.md#configure-your-firewall) is setup so your gateway can be reached from the outside. You can also setup WSS on your Gateway, the steps are on the [Maintenance page](./maintenance.md#configure-your-firewall) below.
```
./nym-gateway run --id <ID>
```
#### Via the Desktop wallet
You can bond your gateway via the Desktop wallet.
## Bonding your Gateway
```admonish info
Before you bond your Gateway, please make sure the [firewall configuration](./maintenance.md#configure-your-firewall) is setup so your Gateway can be reached from the outside. You can also setup [WSS on your Gateway](./maintenance.md#run-web-secure-socket-wss-on-gateway) and [automate](./maintenance.md#vps-setup-and-automation) your Gateway to simplify the operation overhead. We highly recommend to run ny of these steps before bonding to prevent disruption of your Gateway's routing score later on.
```
### Via the Desktop wallet (recommended)
You can bond your Gateway via the Desktop wallet. Make sure your Gateway is running and follow the steps below:
1. Open your wallet, and head to the `Bonding` page, then select the node type `Gateway` and input your node details. Press `Next`.
2. Enter the `Amount`, `Operating cost` and press `Next`.
3. You will be asked to run a the `sign` command with your `gateway` - copy and paste the long signature as the value of `--contract-msg` and run it.
3. You will be asked to run a the `sign` command with your `gateway` - copy and paste the long signature `<PAYLOAD_GENERATED_BY_THE_WALLET>` and paste it as a value of `--contract-msg` in the following command:
```
./nym-gateway sign --id <YOUR_ID> --contract-msg <PAYLOAD_GENERATED_BY_THE_WALLET>
```
It will look something like this:
It will look something like this (as `<YOUR_ID>` we used `supergateway`):
~~~admonish example collapsible=true title="Console output"
```
@@ -209,40 +219,38 @@ It will look something like this:
|_| |_|\__, |_| |_| |_|
|___/
(nym-gateway - version v1.1.31)
(nym-gateway - version v1.1.<XX>)
>>> attempting to sign 2Mf8xYytgEeyJke9LA7TjhHoGQWNBEfgHZtTyy2krFJfGHSiqy7FLgTnauSkQepCZTqKN5Yfi34JQCuog9k6FGA2EjsdpNGAWHZiuUGDipyJ6UksNKRxnFKhYW7ri4MRduyZwbR98y5fQMLAwHne1Tjm9cXYCn8McfigNt77WAYwBk5bRRKmC34BJMmWcAxphcLES2v9RdSR68tkHSpy2C8STfdmAQs3tZg8bJS5Qa8pQdqx14TnfQAPLk3QYCynfUJvgcQTrg29aqCasceGRpKdQ3Tbn81MLXAGAs7JLBbiMEAhCezAr2kEN8kET1q54zXtKz6znTPgeTZoSbP8rzf4k2JKHZYWrHYF9JriXepuZTnyxAKAxvGFPBk8Z6KAQi33NRQkwd7MPyttatHna6kG9x7knffV6ebGzgRBf7NV27LurH8x4L1uUXwm1v1UYCA1WSBQ9Pp2JW69k5v5v7G9gBy8RUcZnMbeL26Qqb8WkuGcmuHhaFfoqSfV7PRHPpPT4M8uRqUyR4bjUtSJJM1yh6QSeZk9BEazzoJqPeYeGoiFDZ3LMj2jesbJweQR4caaYuRczK92UGSSqu9zBKmE45a
>>> decoding the message...
>>> message to sign: {"nonce":0,"algorithm":"ed25519","message_type":"gateway-bonding","content":{"sender":"n1ewmme88q22l8syvgshqma02jv0vqrug9zq9dy8","proxy":null,"funds":[{"denom":"unym","amount":"100000000"}],"data":{"gateway":{"host":"62.240.134.189","mix_port":1789,"clients_port":9000,"location":"62.240.134.189","sphinx_key":"FKbuN7mPdoCG9jA3CkAfXxC5X4rHhqeMVtmfRtJ3cFZd","identity_key":"3RoAhR8gEdfBETMjm2vbMFzKddxXDdE9ygBAnJHWqSzD","version":"1.1.13"}}}}
>>> The base58-encoded signature is:
2SPDjLjX4b6XEtkgG7yD8Znsb1xycL1edFvRK4JcVnPsM9k6HXEUUeVS6rswRiYxoj1bMgiRKyPDwiksiuyxu8Xi
```
~~~
* Copy the resulting signature:
```
>>> The base58-encoded signature is:
```sh
# >>> The base58-encoded signature is:
2SPDjLjX4b6XEtkgG7yD8Znsb1xycL1edFvRK4JcVnPsM9k6HXEUUeVS6rswRiYxoj1bMgiRKyPDwiksiuyxu8Xi
```
* And paste it into the wallet nodal, press `Next` and confirm the transaction.
![Paste Signature](../images/wallet-screenshots/wallet-gateway-sign.png)
![Paste Signature](../images/wallet-screenshots/wallet-gateway-sign.png)
*This image is just an example, copy-paste your own base58-encoded signature*
* Your gateway is now bonded.
* Your Gateway is now bonded.
> You are asked to `sign` a transaction on bonding so that the Mixnet smart contract is able to map your Nym address to your node. This allows us to create a nonce for each account and defend against replay attacks.
#### Via the CLI (power users)
If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.html#bond-a-mix-node) docs.
### Via the CLI (power users)
### Running your gateway
The `run` command starts the gateway:
If you want to bond your Gateway via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.html#bond-a-mix-node) docs.
```
./nym-gateway run --id <ID>
```
## Maintenance
For gateway upgrade, firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md)
For Gateway upgrade, firewall setup, port configuration, API endpoints, VPS suggestions, automation, WSS setup and more, see the [maintenance page](./maintenance.md)
@@ -16,27 +16,38 @@ For example `./target/debug/nym-network-requester --no-banner build-info --outpu
## Upgrading your node
> The process is the similar for mix node, gateway and network requester. In the following steps we use a placeholder `<NODE>` in the commands, please change it for the type of node you want to upgrade. Any particularities for the given type of node are included.
> The process is the similar for Mix Node, Gateway and Network Requester. In the following steps we use a placeholder `<NODE>` in the commands, please change it for the type of node you want to upgrade. Any particularities for the given type of node are included.
Upgrading your node is a two-step process:
* Updating the binary and `~/.nym/<NODE>/<YOUR_ID>/config/config.toml` on your VPS
* Updating the node information in the [mixnet smart contract](https://nymtech.net/docs/nyx/mixnet-contract.html). **This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net)**.
### Step 1: Upgrading your binary
Follow these steps to upgrade your mix node binary and update its config file:
* pause your mix node process.
* replace the existing binary with the newest binary (which you can either [compile yourself](https://nymtech.net/docs/binaries/building-nym.html) or grab from our [releases page](https://github.com/nymtech/nym/releases)).
* re-run `init` with the same values as you used initially. **This will just update the config file, it will not overwrite existing keys**.
* restart your mix node process with the new binary.
Follow these steps to upgrade your Node binary and update its config file:
* Pause your node process.
- if you see the terminal window with your node, press `ctrl + c`
- if you run it as `systemd` service, run: `systemctl stop nym-<NODE>.service`
* Replace the existing `<NODE>` binary with the newest binary (which you can either [compile yourself](https://nymtech.net/docs/binaries/building-nym.html) or grab from our [releases page](https://github.com/nymtech/nym/releases)).
* Re-run `init` with the same values as you used initially for your `<NODE>` ([Mix Node](./mix-node-setup.md#initialising-your-mix-node), [Gateway](./gateway-setup.md#initialising-your-gateway)) . **This will just update the config file, it will not overwrite existing keys**.
* Restart your node process with the new binary:
- if your node is not automitized, just `run` your `<NODE>` with `./nym-<NODE> run --id <ID>`. Here are exact guidelines for [Mix Node](./mix-node-setup.md#running-your-mix-node) and [Gateway](./gateway-setup.md#running-your-gateway).
- if you automatized your node via systemd (recommended) run:
```sh
systemctl daemon-reload # to pickup the new unit file
systemctl start nym-<NODE>.service
journalctl -f -u <NODE>.service # to monitor log of you node
```
> In case of a network requester this is all all, the following step is only for mix nodes and gateways.
If these steps are too difficult and you prefer to just run a script, you can use [ExploreNYM script](https://github.com/ExploreNYM/bash-tool) or one done by [Nym developers](https://gist.github.com/tommyv1987/4dca7cc175b70742c9ecb3d072eb8539).
> In case of a Network Requester this is all, the following step is only for Mix Nodes and Gateways.
### Step 2: Updating your node information in the smart contract
Follow these steps to update the information about your `<NODE>` which is publicly available from the [Nym API](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [mixnet explorer](https://explorer.nymtech.net).
Follow these steps to update the information about your `<NODE>` which is publicly available from the [`nym-api`](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [Mixnet explorer](https://explorer.nymtech.net).
You can either do this graphically via the Desktop Wallet, or the CLI.
### Updating node information via the Desktop Wallet
### Updating node information via the Desktop Wallet (recommended)
* Navigate to the `Bonding` page and click the `Node Settings` link in the top right corner:
![Bonding page](../images/wallet-screenshots/bonding.png)
@@ -53,9 +64,9 @@ If you want to bond your `<NODE>` via the CLI, then check out the [relevant sect
In the previous version of the network-requester, users were required to run a nym-client along side it to function. As of `v1.1.10`, the network-requester now has a nym client embedded into the binary, so it can run standalone.
If you are running an existing network requester registered with nym-connect, upgrading requires you move your old keys over to the new network requester configuration. We suggest following these instructions carefully to ensure a smooth transition.
If you are running an existing Network Requester registered with nym-connect, upgrading requires you move your old keys over to the new Network Requester configuration. We suggest following these instructions carefully to ensure a smooth transition.
Initiate the new network requester:
Initiate the new Network Requester:
```sh
nym-network-requester init --id <YOUR_ID>
@@ -184,13 +195,13 @@ exit 0
```
Although your gateway is Now ready to use its `wss_port`, your server may not be ready - the following commands will allow you to set up a properly configured firewall using `ufw`:
Although your Gateway is Now ready to use its `wss_port`, your server may not be ready - the following commands will allow you to set up a properly configured firewall using `ufw`:
```sh
ufw allow 9001/tcp
```
Lastly don't forget to restart your Gateway, now the API will render the WSS details for this gateway:
Lastly don't forget to restart your Gateway, now the API will render the WSS details for this Gateway:
### WSS on a new Gateway
@@ -203,7 +214,7 @@ Another flag `--public-ips` is required; it's a comma separated list of IPs t
If the operator wishes to run WSS, an optional `--hostname` flag is also required, that can be something like `mainnet-gateway2.nymtech.net`. Make sure to enable all necessary [ports](maintenance.md#configure-your-firewall) on the Gateway.
The gateway will then be accessible on something like: *http://85.159.211.99:8080/api/v1/swagger/index.html*
The Gateway will then be accessible on something like: *http://85.159.211.99:8080/api/v1/swagger/index.html*
Are you seeing something like: *this node attempted to announce an invalid public address: 0.0.0.0.*?
@@ -236,7 +247,7 @@ sudo ufw status
Finally open your `<NODE>` p2p port, as well as ports for ssh and ports for verloc and measurement pings:
```sh
# for mix node, gateway and network requester
# for Mix Node, Gateway and Network Requester
sudo ufw allow 1789,1790,8000,9000,9001,22/tcp
# In case of reverse proxy for the Gateway swagger page add:
@@ -257,7 +268,7 @@ For more information about your node's port configuration, check the [port refer
### Automating your node with nohup, tmux and systemd
Although its not totally necessary, it's useful to have the mix node automatically start at system boot time.
Although its not totally necessary, it's useful to have the Mix Node automatically start at system boot time.
#### nohup
@@ -287,7 +298,7 @@ In case it didn't work for your distribution, see how to build `tmux` from [vers
**Running tmux**
No when you installed tmux on your VPS, let's run a mix node on tmux, which allows you to detach your terminal and let your `<NODE>` run on its own on the VPS.
No when you installed tmux on your VPS, let's run a Mix Node on tmux, which allows you to detach your terminal and let your `<NODE>` run on its own on the VPS.
* Pause your `<NODE>`
* Start tmux with the command
@@ -310,7 +321,7 @@ tmux attach-session
Here's a systemd service file to do that:
##### For mix node
##### For Mix Node
```ini
[Unit]
@@ -354,7 +365,7 @@ WantedBy=multi-user.target
* Put the above file onto your system at `/etc/systemd/system/nym-gateway.service`.
##### For Network requester
##### For Network Requester
```ini
[Unit]
@@ -433,20 +444,20 @@ systemctl daemon-reload # to pickup the new unit file
```
```sh
# for mix node
# for Mix Node
systemctl enable nym-mixnode.service
# for gateway
# for Gateway
systemctl enable nym-gateway.service
```
Start your node:
```sh
# for mix node
# for Mix Node
service nym-mixnode start
# for gateway
# for Gateway
service nym-gateway start
```
@@ -477,7 +488,7 @@ This lets your operating system know it's ok to reload the service configuration
Linux machines limit how many open files a user is allowed to have. This is called a `ulimit`.
`ulimit` is 1024 by default on most systems. It needs to be set higher, because mix nodes make and receive a lot of connections to other nodes.
`ulimit` is 1024 by default on most systems. It needs to be set higher, because Mix Nodes make and receive a lot of connections to other nodes.
If you see errors such as:
@@ -491,12 +502,12 @@ This means that the operating system is preventing network connections from bein
> Replace `<NODE>` variable with `nym-mixnode`, `nym-gateway` or `nym-network-requester` according the node you running on your machine.
The ulimit setup is relevant for maintenance of nym mix node only.
The ulimit setup is relevant for maintenance of Nym Mix Node only.
Query the `ulimit` of your `<NODE>` with:
```sh
# for nym-mixnode, nym-gateway and nym-network requester:
# for nym-mixnode, nym-gateway and nym-network-requester:
grep -i "open files" /proc/$(ps -A -o pid,cmd|grep <NODE> | grep -v grep |head -n 1 | awk '{print $1}')/limits
# for nyx validator:
@@ -533,7 +544,7 @@ echo "DefaultLimitNOFILE=65535" >> /etc/systemd/system.conf
Reboot your machine and restart your node. When it comes back, use:
```sh
# for nym-mixnode, nym-gateway and nym-network requester:
# for nym-mixnode, nym-gateway and nym-network-requester:
cat /proc/$(pidof <NODE>)/limits | grep "Max open files"
# for validator
@@ -543,7 +554,7 @@ Make sure the limit has changed to 65535.
#### Set the ulimit on `non-systemd` based distributions
In case you chose tmux option for mix node automatization, see your `ulimit` list by running:
In case you chose tmux option for Mix Node automation, see your `ulimit` list by running:
```sh
ulimit -a
@@ -568,13 +579,13 @@ username hard nofile 4096
username soft nofile 4096
```
Then reboot your server and restart your mix node.
Then reboot your server and restart your Mix Node.
## Moving a node
In case of a need to move a node from one machine to another and avoiding to lose the delegation, here are few steps how to do it.
The following examples transfers a mix node (in case of other nodes, change the `mixnodes` in the command for the `<NODE>` of your desire.
The following examples transfers a Mix Node (in case of other nodes, change the `mixnodes` in the command for the `<NODE>` of your desire.
* Pause your node process.
@@ -587,7 +598,7 @@ Assuming both machines are remote VPS.
# in case none of the nym configs was created previously
mkdir ~/.nym
#in case no nym mix node was initialized previously
#in case no nym Mix Node was initialized previously
mkdir ~/.nym/mixnodes
```
* Move the node data (keys) and config file to the new machine by opening a local terminal (as that one's ssh key is authorized in both of the machines) and running:
@@ -614,29 +625,29 @@ ens4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460
The `ens4` interface has the IP `10.126.5.7`. But this isn't the public IP of the machine, it's the IP of the machine on Google's internal network. Google uses virtual routing, so the public IP of this machine is something else, maybe `36.68.243.18`.
`./nym-mixnode init --host 10.126.5.7`, initalises the mix node, but no packets will be routed because `10.126.5.7` is not on the public internet.
`./nym-mixnode init --host 10.126.5.7`, initalises the Mix Node, but no packets will be routed because `10.126.5.7` is not on the public internet.
Trying `nym-mixnode init --host 36.68.243.18`, you'll get back a startup error saying `AddrNotAvailable`. This is because the mix node doesn't know how to bind to a host that's not in the output of `ifconfig`.
Trying `nym-mixnode init --host 36.68.243.18`, you'll get back a startup error saying `AddrNotAvailable`. This is because the Mix Node doesn't know how to bind to a host that's not in the output of `ifconfig`.
The right thing to do in this situation is to init with a command:
```sh
./nym-mixnode init --host 10.126.5.7 --announce-host 36.68.243.18
```
This will bind the mix node to the available host `10.126.5.7`, but announce the mix node's public IP to the directory server as `36.68.243.18`. It's up to you as a node operator to ensure that your public and private IPs match up properly.
This will bind the Mix Node to the available host `10.126.5.7`, but announce the Mix Node's public IP to the directory server as `36.68.243.18`. It's up to you as a node operator to ensure that your public and private IPs match up properly.
To find the right IP configuration, contact your VPS provider for support.
## Nym API (previously 'Validator API') endpoints
Numerous API endpoints are documented on the Nym API (previously 'Validator API')'s [Swagger Documentation](https://validator.nymtech.net/api/swagger/index.html). There you can also try out various requests from your browser, and download the response from the API. Swagger will also show you what commands it is running, so that you can run these from an app or from your CLI if you prefer.
### Mix node Reward Estimation API endpoint
### Mix Node Reward Estimation API endpoint
The Reward Estimation API endpoint allows mix node operators to estimate the rewards they could earn for running a Nym mix node with a specific `MIX_ID`.
The Reward Estimation API endpoint allows Mix Node operators to estimate the rewards they could earn for running a Nym Mix Node with a specific `MIX_ID`.
> The `<MIX_ID>` can be found in the "Mix ID" column of the [Network Explorer](https://explorer.nymtech.net/network-components/mixnodes/active).
The endpoint is a particularly common for mix node operators as it can provide an estimate of potential earnings based on factors such as the amount of traffic routed through the mix node, the quality of the mix node's performance, and the overall demand for mixnodes in the network. This information can be useful for mix node operators in deciding whether or not to run a mix node and in optimizing its operations for maximum profitability.
The endpoint is a particularly common for Mix Node operators as it can provide an estimate of potential earnings based on factors such as the amount of traffic routed through the Mix Node, the quality of the Mix Node's performance, and the overall demand for Mix Nodes in the network. This information can be useful for Mix Node operators in deciding whether or not to run a Mix Node and in optimizing its operations for maximum profitability.
Using this API endpoint returns information about the Reward Estimation:
@@ -657,15 +668,15 @@ Query Response:
> The unit of value is measured in `uNYM`.
- `estimated_total_node_reward` - An estimate of the total amount of rewards that a particular mix node can expect to receive during the current epoch. This value is calculated by the Nym Validator based on a number of factors, including the current state of the network, the number of mix nodes currently active in the network, and the amount of network traffic being processed by the mix node.
- `estimated_total_node_reward` - An estimate of the total amount of rewards that a particular Mix Node can expect to receive during the current epoch. This value is calculated by the Nym Validator based on a number of factors, including the current state of the network, the number of Mix Nodes currently active in the network, and the amount of network traffic being processed by the Mix Node.
- `estimated_operator_reward` - An estimate of the amount of rewards that a particular mix node operator can expect to receive. This value is calculated by the Nym Validator based on a number of factors, including the amount of traffic being processed by the mix node, the quality of service provided by the mix node, and the operator's stake in the network.
- `estimated_operator_reward` - An estimate of the amount of rewards that a particular Mix Node operator can expect to receive. This value is calculated by the Nym Validator based on a number of factors, including the amount of traffic being processed by the Mix Node, the quality of service provided by the Mix Node, and the operator's stake in the network.
- `estimated_delegators_reward` - An estimate of the amount of rewards that mix node delegators can expect to receive individually. This value is calculated by the Nym Validator based on a number of factors, including the amount of traffic being processed by the mix node, the quality of service provided by the mix node, and the delegator's stake in the network.
- `estimated_delegators_reward` - An estimate of the amount of rewards that Mix Node delegators can expect to receive individually. This value is calculated by the Nym Validator based on a number of factors, including the amount of traffic being processed by the Mix Node, the quality of service provided by the Mix Node, and the delegator's stake in the network.
- `estimated_node_profit` - An estimate of the profit that a particular mix node operator can expect to earn. This value is calculated by subtracting the mix node operator's `operating_costs` from their `estimated_operator_reward` for the current epoch.
- `estimated_node_profit` - An estimate of the profit that a particular Mix node operator can expect to earn. This value is calculated by subtracting the Mix Node operator's `operating_costs` from their `estimated_operator_reward` for the current epoch.
- `estimated_operator_cost` - An estimate of the total cost that a particular mix node operator can expect to incur for their participation. This value is calculated by the Nym Validator based on a number of factors, including the cost of running a mix node, such as server hosting fees, and other expenses associated with operating the mix node.
- `estimated_operator_cost` - An estimate of the total cost that a particular Mix Node operator can expect to incur for their participation. This value is calculated by the Nym Validator based on a number of factors, including the cost of running a Mix Node, such as server hosting fees, and other expenses associated with operating the Mix Node.
### Validator: Installing and configuring nginx for HTTPS
#### Setup
@@ -785,7 +796,7 @@ go_memstats_gc_sys_bytes 1.3884192e+07
## Ports
All `<NODE>`-specific port configuration can be found in `$HOME/.nym/<NODE>/<YOUR_ID>/config/config.toml`. If you do edit any port configs, remember to restart your client and node processes.
### Mix node port reference
### Mix Node port reference
| Default port | Use |
| ------------ | ------------------------- |
| `1789` | Listen for Mixnet traffic |
@@ -800,7 +811,7 @@ All `<NODE>`-specific port configuration can be found in `$HOME/.nym/<NODE>/<YOU
| `9000` | Listen for Client traffic |
| `9001` | WSS |
### Network requester port reference
### Network Requester port reference
| Default port | Use |
|--------------|---------------------------|
@@ -1,6 +1,6 @@
# Mix Nodes
> The Nym mix node binary was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first.
> The Nym Mix Node binary was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first.
> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets.
@@ -13,11 +13,11 @@ The `nym-mix node` binary is currently one point version ahead of the rest of th
## Preliminary steps
Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your mix node.
Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your Mix Node.
## Mix node setup
Now that you have built the [codebase](../binaries/building-nym.md), set up your [wallet](https://nymtech.net/docs/wallet/desktop-wallet.html), and have a VPS with the `nym-mix node` binary, you can set up your mix node with the instructions below.
Now that you have built the [codebase](../binaries/building-nym.md), set up your [wallet](https://nymtech.net/docs/wallet/desktop-wallet.html), and have a VPS with the `nym-mix node` binary, you can set up your Mix Node with the instructions below.
To begin, move to `/target/release` directory from which you run the node commands:
@@ -49,7 +49,7 @@ You can also check the various arguments required for individual commands with:
> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment.
### Initialising your mix node
### Initialising your Mix Node
To check available configuration options for initializing your node use:
@@ -59,125 +59,30 @@ To check available configuration options for initializing your node use:
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../target/release/nym-mixnode init --help -->
<!-- cmdrun ../../../../target/release/nym-mixnode init --help -->
```
~~~
Initalise your mix node with the following command, replacing the value of `--id` with the moniker you wish to give your mix node. Your `--host` must be publicly routable on the internet in order to mix packets, and can be either an Ipv4 or IPv6 address. The `$(curl -4 https://ifconfig.me)` command returns your IP automatically using an external service. If you enter your IP address manually, enter it **without** any port information.
Initialise your Mix Node with the following command, replacing the value of `--id` with the moniker you wish to give your Mix Node. Your `--host` must be publicly routable on the internet in order to mix packets, and can be either an Ipv4 or IPv6 address. The `$(curl -4 https://ifconfig.me)` command returns your IP automatically using an external service. If you enter your IP address manually, enter it **without** any port information.
```
./nym-mixnode init --id <NODE_NAME> --host $(curl -4 https://ifconfig.me)
./nym-mixnode init --id <YOUR_ID> --host $(curl -4 https://ifconfig.me)
```
If `<YOUR_ID>` was `my-node`, the output shall look like like this:
<!---serinko: The automatized command did not work, printing the output manually--->
~~~admonish example collapsible=true title="Console output"
```
.nym-mixnode init --id <YOUR_ID> --host $(curl -4 https://ifconfig.me) --wallet-address <WALLET_ADDRESS>
Initialising mixnode <YOUR_ID>...
Saved mixnet identity and sphinx keypairs
2023-06-04T08:20:32.862Z INFO nym_config > Configuration file will be saved to "/home/<USER>/.nym/mixnodes/<YOUR_ID>/config/config.toml"
Saved configuration file to "/home/<USER>/.nym/mixnodes/<YOUR_ID>/config/config.toml"
Mixnode configuration completed.
_ __ _ _ _ __ ___
| '_ \| | | | '_ \ _ \
| | | | |_| | | | | | |
|_| |_|\__, |_| |_| |_|
|___/
(nym-mixnode - version v1.1.29)
Identity Key: DhmUYedPZvhP9MMwXdNpPaqCxxTQgjAg78s2nqtTTiNF","version":"v1.1.29"},"cost_params
Sphinx Key: CfZSy1jRfrfiVi9JYexjFWPqWkKoY72t7NdpWaq37K8Z
Host: 62.240.134.189 (bind address: 62.240.134.189)
Version: v1.1.29
Mix Port: 1789, Verloc port: 1790, Http Port: 8000
<!-- cmdrun ../../../../target/release/nym-mixnode init --id my-node --host $(curl -4 https://ifconfig.me) -->
```
~~~
> The `init` command will refuse to destroy existing mix node keys.
> The `init` command will refuse to destroy existing Mix Node keys.
During the `init` process you will have the option to change the `http_api`, `verloc` and `mixnode` ports from their default settings. If you wish to change these in the future you can edit their values in the `config.toml` file created by the initialization process, which is located at `~/.nym/mixnodes/<YOUR_ID>/`.
### Bonding your mix node
```admonish caution
From `v1.1.3`, if you unbond your mix node that means you are leaving the mixnet and you will lose all your delegations (permanently). You can join again with the same identity key, however, you will start with **no delegations**.
```
#### Bond via the Desktop wallet (recommended)
You can bond your mix node via the Desktop wallet.
* Open your wallet, and head to the `Bond` page, then select the node type `Mixnode` and input your node details. Press `Next`.
* Enter the `Amount`, `Operating cost` and `Profit margin` and press `Next`.
* You will be asked to run a the `sign` command with your `gateway` - copy and paste the long signature as the value of `--contract-msg` and run it.
```
./nym-mixnode sign --id <YOUR_ID> --contract-msg <PAYLOAD_GENERATED_BY_THE_WALLET>
```
It will look something like this:
~~~admonish example collapsible=true title="Console output"
```
./nym-mixnode sign --id upgrade_test --contract-msg 22Z9wt4PyiBCbMiErxj5bBa4VCCFsjNawZ1KnLyMeV9pMUQGyksRVANbXHjWndMUaXNRnAuEVJW6UCxpRJwZe788hDt4sicsrv7iAXRajEq19cWPVybbUqgeo76wbXbCbRdg1FvVKgYZGZZp8D72p5zWhKSBRD44qgCrqzfV1SkiFEhsvcLUvZATdLRocAUL75KmWivyRiQjCE1XYEWyRH9yvRYn4TymWwrKVDtEB63zhHjATN4QEi2E5qSrSbBcmmqatXsKakbgSbQoLsYygcHx7tkwbQ2HDYzeiKP1t16Rhcjn6Ftc2FuXUNnTcibk2LQ1hiqu3FAq31bHUbzn2wiaPfm4RgqTwGM4eqnjBofwR3251wQSxbYwKUYwGsrkweRcoPuEaovApR9R19oJ7GVG5BrKmFwZWX3XFVuECe8vt1x9MY7DbQ3xhAapsHhThUmzN6JPPU4qbQ3PdMt3YVWy6oRhap97ma2dPMBaidebfgLJizpRU3Yu7mtb6E8vgi5Xnehrgtd35gitoJqJUY5sB1p6TDPd6vk3MVU1zqusrke7Lvrud4xKfCLqp672Bj9eGb2wPwow643CpHuMkhigfSWsv9jDq13d75EGTEiprC2UmWTzCJWHrDH7ka68DZJ5XXAW67DBewu7KUm1jrJkNs55vS83SWwm5RjzQLVhscdtCH1Bamec6uZoFBNVzjs21o7ax2WHDghJpGMxFi6dmdMCZpqn618t4
_ __ _ _ _ __ ___
| '_ \| | | | '_ \ _ \
| | | | |_| | | | | | |
|_| |_|\__, |_| |_| |_|
|___/
(nym-mixnode - version v1.1.29)
>>> attempting to sign 22Z9wt4PyiBCbMiErxj5bBa4VCCFsjNawZ1KnLyMeV9pMUQGyksRVANbXHjWndMUaXNRnAuEVJW6UCxpRJwZe788hDt4sicsrv7iAXRajEq19cWPVybbUqgeo76wbXbCbRdg1FvVKgYZGZZp8D72p5zWhKSBRD44qgCrqzfV1SkiFEhsvcLUvZATdLRocAUL75KmWivyRiQjCE1XYEWyRH9yvRYn4TymWwrKVDtEB63zhHjATN4QEi2E5qSrSbBcmmqatXsKakbgSbQoLsYygcHx7tkwbQ2HDYzeiKP1t16Rhcjn6Ftc2FuXUNnTcibk2LQ1hiqu3FAq31bHUbzn2wiaPfm4RgqTwGM4eqnjBofwR3251wQSxbYwKUYwGsrkweRcoPuEaovApR9R19oJ7GVG5BrKmFwZWX3XFVuECe8vt1x9MY7DbQ3xhAapsHhThUmzN6JPPU4qbQ3PdMt3YVWy6oRhap97ma2dPMBaidebfgLJizpRU3Yu7mtb6E8vgi5Xnehrgtd35gitoJqJUY5sB1p6TDPd6vk3MVU1zqusrke7Lvrud4xKfCLqp672Bj9eGb2wPwow643CpHuMkhigfSWsv9jDq13d75EGTEiprC2UmWTzCJWHrDH7ka68DZJ5XXAW67DBewu7KUm1jrJkNs55vS83SWwm5RjzQLVhscdtCH1Bamec6uZoFBNVzjs21o7ax2WHDghJpGMxFi6dmdMCZpqn618t4
>>> decoding the message...
>>> message to sign: {"nonce":0,"algorithm":"ed25519","message_type":"mixnode-bonding","content":{"sender":"n1eufxdlgt0puwrwptgjfqne8pj4nhy2u5ft62uq","proxy":null,"funds":[{"denom":"unym","amount":"100000000"}],"data":{"mix_node":{"host":"62.240.134.189","mix_port":1789,"verloc_port":1790,"http_api_port":8000,"sphinx_key":"CfZSy1jRfrfiVi9JYexjFWPqWkKoY72t7NdpWaq37K8Z","identity_key":"DhmUYedPZvhP9MMwXdNpPaqCxxTQgjAg78s2nqtTTiNF","version":"1.1.14"},"cost_params":{"profit_margin_percent":"0.1","interval_operating_cost":{"denom":"unym","amount":"40000000"}}}}}
```
~~~
* Copy the resulting signature:
```
>>> The base58-encoded signature is:
2GbKcZVKFdpi3sR9xoJWzwPuGdj3bvd7yDtDYVoKfbTWdpjqAeU8KS5bSftD5giVLJC3gZiCg2kmEjNG5jkdjKUt
```
* And paste it into the wallet nodal, press `Next` and confirm the transaction.
![Paste Signature](../images/wallet-screenshots/wallet-sign.png)
* Your node will now be bonded and ready to mix at the beginning of the next epoch (at most 1 hour).
> You are asked to `sign` a transaction on bonding so that the mixnet smart contract is able to map your nym address to your node. This allows us to create a nonce for each account and defend against replay attacks.
#### Bond via the CLI (power users)
If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.html#bond-a-mix-node) docs.
### Running your mix node
Now you've bonded your mix node, run it with:
```
./nym-mixnode run --id <YOUR_ID>
```
If everything worked, you'll see your node running on the either the [Sandbox testnet network explorer](https://sandbox-explorer.nymtech.net) or the [mainnet network explorer](https://explorer.nymtech.net), depending on which network you're running.
Note that your node's public identity key is displayed during startup, you can use it to identify your node in the list.
Have a look at the saved configuration files in `$HOME/.nym/mixnodes/` to see more configuration options.
## Node Description (optional)
In order to easily identify your node via human-readable information later on in the development of the testnet when delegated staking is implemented, you can `describe` your mix node with the following command:
In order to easily identify your node via human-readable information later on, you can `describe` your Mix Node with the following command:
```
./nym-mixnode describe --id <YOUR_ID>
@@ -193,15 +98,85 @@ link = "https://nymtech.net"
location = "Giza, Egypt"
```
> Remember to restart your mix node process in order for the new description to be propagated.
> Remember to restart your `nym-mix-node` process in order for the new description to be propagated.
## Running your Mix Node
Run your Mix Node with:
```
./nym-mixnode run --id <YOUR_ID>
```
Have a look at the saved configuration files in `$HOME/.nym/mixnodes/` to see more configuration options.
## Bonding your Mix Node
```admonish caution
From `v1.1.3`, if you unbond your Mix Node that means you are leaving the mixnet and you will lose all your delegations (permanently). You can join again with the same identity key, however, you will start with **no delegations**.
```
To initialise, run and bond your Mix Node are the minimum steps to do in order for your Mix Node to work. However we recommend to do a few more steps before bonding. These steps will make it easier for you as a node operator on a long run as well as for others to possibly delegate Nym tokens to your Mix Node. These steps are:
- [Describe your Mix Node](./mix-node-setup.md#node-description-optional)
- [Configure your firewall](./maintenance.md#configure-your-firewall)
- [Automate your Mix Node](./maintenance.md#vps-setup-and-automation)
- Set the [ulimit](./maintenance.md#set-the-ulimit-via-systemd-service-file), in case you haven't automated with [systemd](./maintenance.md#set-the-ulimit-on-non-systemd-based-distributions)
### Bond via the Desktop wallet (recommended)
You can bond your Mix Node via the Desktop wallet.
* Open your wallet, and head to the `Bond` page, then select the node type `Mixnode` and input your node details. Press `Next`.
* Enter the `Amount`, `Operating cost` and `Profit margin` and press `Next`.
* You will be asked to run a the `sign` command with your `mixnode` - copy and paste the long signature as the value of `--contract-msg` and run it.
```
./nym-mixnode sign --id <YOUR_ID> --contract-msg <PAYLOAD_GENERATED_BY_THE_WALLET>
```
It will look something like this:
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../target/release/nym-mixnode init --id my-node --host $(curl -4 https://ifconfig.me) -->
<!-- cmdrun ../../../../target/release/nym-mixnode sign --id my-node --contract-msg 22Z9wt4PyiBCbMiErxj5bBa4VCCFsjNawZ1KnLyMeV9pMUQGyksRVANbXHjWndMUaXNRnAuEVJW6UCxpRJwZe788hDt4sicsrv7iAXRajEq19cWPVybbUqgeo76wbXbCbRdg1FvVKgYZGZZp8D72p5zWhKSBRD44qgCrqzfV1SkiFEhsvcLUvZATdLRocAUL75KmWivyRiQjCE1XYEWyRH9yvRYn4TymWwrKVDtEB63zhHjATN4QEi2E5qSrSbBcmmqatXsKakbgSbQoLsYygcHx7tkwbQ2HDYzeiKP1t16Rhcjn6Ftc2FuXUNnTcibk2LQ1hiqu3FAq31bHUbzn2wiaPfm4RgqTwGM4eqnjBofwR3251wQSxbYwKUYwGsrkweRcoPuEaovApR9R19oJ7GVG5BrKmFwZWX3XFVuECe8vt1x9MY7DbQ3xhAapsHhThUmzN6JPPU4qbQ3PdMt3YVWy6oRhap97ma2dPMBaidebfgLJizpRU3Yu7mtb6E8vgi5Xnehrgtd35gitoJqJUY5sB1p6TDPd6vk3MVU1zqusrke7Lvrud4xKfCLqp672Bj9eGb2wPwow643CpHuMkhigfSWsv9jDq13d75EGTEiprC2UmWTzCJWHrDH7ka68DZJ5XXAW67DBewu7KUm1jrJkNs55vS83SWwm5RjzQLVhscdtCH1Bamec6uZoFBNVzjs21o7ax2WHDghJpGMxFi6dmdMCZpqn618t4 -->
```
~~~
* Copy the resulting signature:
```sh
# >>> The base58-encoded signature is:
2bbDJSmSo9r9qdamTNygY297nQTVRyQaxXURuomVcRd7EvG9oEC8uW8fvZZYnDeeC9iWyG9mAbX2K8rWEAxZBro1
```
* And paste it into the wallet nodal, press `Next` and confirm the transaction.
![Paste Signature](../images/wallet-screenshots/wallet-sign.png)
*This image is just an example, copy-paste your own base58-encoded signature*
* Your node will now be bonded and ready to mix at the beginning of the next epoch (at most 1 hour).
> You are asked to `sign` a transaction on bonding so that the Mixnet smart contract is able to map your nym address to your node. This allows us to create a nonce for each account and defend against replay attacks.
If everything worked, you'll see your node running on the either the [Sandbox testnet network explorer](https://sandbox-explorer.nymtech.net) or the [mainnet network explorer](https://explorer.nymtech.net), depending on which network you're running.
Note that your node's public identity key is displayed during startup, you can use it to identify your node in the list.
### Bond via the CLI (power users)
If you want to bond your Mix Node via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.html#bond-a-mix-node) docs.
## Node Families
Node family involves setting up a group of mix nodes that work together to provide greater privacy and security for network communications. This is achieved by having the nodes in the family share information and routes, creating a decentralized network that makes it difficult for third parties to monitor or track communication traffic.
Node family involves setting up a group of Mix Nodes that work together to provide greater privacy and security for network communications. This is achieved by having the nodes in the family share information and routes, creating a decentralized network that makes it difficult for third parties to monitor or track communication traffic.
### Create a Node Family
To create a Node family, you will need to install and configure multiple mix nodes, and then use the CLI to link them together into a family. Once your Node family is up and running, you can use it to route your network traffic through a series of nodes, obscuring the original source and destination of the communication.
To create a Node family, you will need to install and configure multiple Mix Nodes, and then use the CLI to link them together into a family. Once your Node family is up and running, you can use it to route your network traffic through a series of nodes, obscuring the original source and destination of the communication.
You can use either `nym-cli` which can be downloaded from the [release page](https://github.com/nymtech/nym/releases) or compiling `nyxd`.
@@ -214,7 +189,8 @@ Change directory by `cd <PATH>/<TO>/<THE>/<RELEASE>` and run the following on th
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../target/release/nym-mixnode sign --id YOUR_ID --text "TEXT" -->
<!-- cmdrun ../../../../target/release/nym-mixnode init --id YOUR_ID --host $(curl -4 https://ifconfig.me) -->
<!-- cmdrun ../../../../target/release/nym-mixnode sign --id YOUR_ID --text "TEXT" -->
```
~~~
@@ -248,7 +224,8 @@ Change directory by `cd <PATH>/<TO>/<THE>/<RELEASE>` and run the following on th
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../target/release/nym-mixnode sign --id YOUR_ID --text "TEXT" -->
<!-- cmdrun ../../../../target/release/nym-mixnode init --id YOUR_ID --host $(curl -4 https://ifconfig.me) -->
<!-- cmdrun ../../../../target/release/nym-mixnode sign --id YOUR_ID --text "TEXT" -->
```
~~~
@@ -274,7 +251,7 @@ To get the node owner signature, use:
If wanting to leave, run the same initial command as above, followed by:
Using `nym-cli`:
<!---the sting under shall be changed to <NODE_ADDRESS>? --->
```
./nym-cli cosmwasm execute <WALLET_ADDRESS> '{"leave_family": {"signature": "<base58-encoded-signature>","family_head": "<TEXT>","owner_signautre": "<OWNER_IGNATURE_FROM_NODE_TO_LEAVE>"}}' --mnemonic <MNEMONIC_FROM_NODE_TO_LEAVE>
```
@@ -287,7 +264,7 @@ Using `nyxd`:
## Checking that your node is mixing correctly
### Network explorers
Once you've started your mix node and it connects to the validator, your node will automatically show up in the 'Mix nodes' section of either the Nym Network Explorers:
Once you've started your Mix Node and it connects to the validator, your node will automatically show up in the 'Mix Nodes' section of either the Nym Network Explorers:
- [Mainnet](https://explorer.nymtech.net/overview)
- [Sandbox testnet](https://sandbox-explorer.nymtech.net/)
@@ -301,9 +278,7 @@ There are also 2 community explorers which have been created by [Nodes Guru](htt
For more details see [Troubleshooting FAQ](../nodes/troubleshooting.md)
<!---Enter faq link to the information how to higher chances to become a part of an active set--->
## Maintenance
For mix node upgrade, firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md)
For Mix Node upgrade, firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md)
@@ -1,9 +1,9 @@
# Network Requesters
> The Nym network requester was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first.
> Nym Network Requester was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first.
```admonish info
As a result of [Project Smoosh](../faq/smoosh-faq.md), the current version of `nym-gateway` binary also contains `nym-network-requester` functionality which can be enabled [by the operator](./gateway-setup.md#initialising-gateway-with-network-requester). This combination is a basis of Nym exit gateway node - an essential piece in our new setup. Please read more in our [Project Smoosh FAQ](../faq/smoosh-faq.md) and [Exit Gateways Page](../legal/exit-gateway.md). We recommend operators begin to shift their setups to this new combined node, instead of operating two separate binaries.
As a result of [Project Smoosh](../faq/smoosh-faq.md), the current version of `nym-gateway` binary also contains `nym-network-requester` functionality which can be enabled [by the operator](./gateway-setup.md#initialising-gateway-with-network-requester). This combination is a basis of Nym Exit Gateway node - an essential piece in our new setup. Please read more in our [Project Smoosh FAQ](../faq/smoosh-faq.md) and [Exit Gateways Page](../legal/exit-gateway.md). We recommend operators begin to shift their setups to this new combined node, instead of operating two separate binaries.
```
> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets.
@@ -15,23 +15,23 @@ As a result of [Project Smoosh](../faq/smoosh-faq.md), the current version of `n
## Preliminary steps
Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your network requester.
Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your Network Requester.
## Network Requester Whitelist
If you have access to a server, you can run the network requester, which allows Nym users to send outbound requests from their local machine through the mixnet to a server, which then makes the request on their behalf, shielding them (and their metadata) from clearnet, untrusted and unknown infrastructure, such as email or message client servers.
If you have access to a server, you can run the Network Requester, which allows Nym users to send outbound requests from their local machine through the Mixnet to a server, which then makes the request on their behalf, shielding them (and their metadata) from clearnet, untrusted and unknown infrastructure, such as email or message client servers.
By default the network requester is **not** an open proxy (although it can be used as one). It uses a file called `allowed.list` (located in `~/.nym/service-providers/network-requester/<NETWORK-REQUESTER-ID>/`) as a whitelist for outbound requests.
By default the Network Requester is **not** an open proxy (although it can be used as one). It uses a file called `allowed.list` (located in `~/.nym/service-providers/network-requester/<NETWORK-REQUESTER-ID>/`) as a whitelist for outbound requests.
**Note:** If you run network requester as a part of the exit gateway (suggested setup) the `allowed.list` will be stored in `~/.nym/gateways/<ID>/data/network-requester-data/allowed.list`.
**Note:** If you run Network Requester as a part of the Exit Gateway (suggested setup) the `allowed.list` will be stored in `~/.nym/gateways/<ID>/data/network-requester-data/allowed.list`.
Any request to a URL which is not on this list will be blocked.
On startup, if this file is not present, the requester will grab the default whitelist from [Nym's default list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) automatically.
This default whitelist is useful for knowing that the majority of Network requesters are able to support certain apps 'out of the box'.
This default whitelist is useful for knowing that the majority of Network Requesters are able to support certain apps 'out of the box'.
**Operators of a network requester are of course free to edit this file and add the URLs of services they wish to support to it!** You can find instructions below on adding your own URLs or IPs to this list.
**Operators of a Network Requester are of course free to edit this file and add the URLs of services they wish to support to it!** You can find instructions below on adding your own URLs or IPs to this list.
The domains and IPs on the default whitelist can be broken down by application as follows:
@@ -109,14 +109,14 @@ alephium.org
## Network Requester Directory
You can find a list of Network requesters running the default whitelist in the [explorer](https://explorer.nymtech.net/network-components/service-providers). This list comprises of the NRs running as infrastructure for NymConnect.
You can find a list of Network Requesters running the default whitelist in the [explorer](https://explorer.nymtech.net/network-components/service-providers). This list comprises of the NRs running as infrastructure for NymConnect.
> We are currently working on a smart-contract based solution more in line with how Mix nodes and Gateways announce themselves to the network.
> We are currently working on a smart-contract based solution more in line with how Mix Nodes and Gateways announce themselves to the network.
## Viewing command help
```admonish info
If you run your network requester as a part of your exit gateway according to the suggested setup, please skip this part of the page and read about [exit gateway setup](./gateway-setup.md#initialising-gateway-with-network-requester) instead.
If you run your Network Requester as a part of your Exit Gateway according to the suggested setup, please skip this part of the page and read about [Exit Gateway setup](./gateway-setup.md#initialising-gateway-with-network-requester) instead.
```
To begin, move to `/target/release` directory from which you run the node commands:
@@ -141,9 +141,9 @@ You can check the required parameters for available commands by running:
> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment.
## Initializing and running your network requester
## Initializing and running your Network Requester
The network-requester needs to be initialized before it can be run. This is required for the embedded nym-client to connect successfully to the mixnet. We want to specify an `<ID>` using the `--id` command and give it a value of your choice. The following command will achieve that:
The Network Requester needs to be initialized before it can be run. This is required for the embedded nym-client to connect successfully to the Mixnet. We want to specify an `<ID>` using the `--id` command and give it a value of your choice. The following command will achieve that:
```
./nym-network-requester init --id <YOUR_ID>
@@ -164,11 +164,11 @@ Now that we have initialized our network-requester, we can start it with the fol
./nym-network-requester run --id <YOUR_ID>
```
## Using your network requester
## Using your Network Requester
The next thing to do is use your requester, share its address with friends (or whoever you want to help privacy-enhance their app traffic). Is this safe to do? If it was an open proxy, this would be unsafe, because any Nym user could make network requests to any system on the internet.
To make things a bit less stressful for administrators, the Network Requester drops all incoming requests by default. In order for it to make requests, you need to add specific domains to the `allowed.list` file at `$HOME/.nym/service-providers/network-requester/allowed.list` or if network requester is ran as a part of [exit gateway](./gateway-setup.md#initialising-gateway-with-network-requester), the `allowed.list` will be stored in `~/.nym/gateways/<ID>/data/network-requester-data/allowed.list`
To make things a bit less stressful for administrators, the Network Requester drops all incoming requests by default. In order for it to make requests, you need to add specific domains to the `allowed.list` file at `$HOME/.nym/service-providers/network-requester/allowed.list` or if Network Requester is ran as a part of [Exit Gateway](./gateway-setup.md#initialising-gateway-with-network-requester), the `allowed.list` will be stored in `~/.nym/gateways/<ID>/data/network-requester-data/allowed.list`
### Global vs local allow lists
Your Network Requester will check for a domain against 2 lists before allowing traffic through for a particular domain or IP.
@@ -177,34 +177,34 @@ Your Network Requester will check for a domain against 2 lists before allowing t
* The second is the local `allowed.list` file.
### Supporting custom domains with your network requester
It is easy to add new domains and services to your network requester - simply find out which endpoints (both URLs and raw IP addresses are supported) you need to whitelist, and then add these endpoints to your `allowed.list`.
### Supporting custom domains with your Network Requester
It is easy to add new domains and services to your Network Requester - simply find out which endpoints (both URLs and raw IP addresses are supported) you need to whitelist, and then add these endpoints to your `allowed.list`.
> In order to keep things more organised, you can now use comments in the `allow.list` like the example at the top of this page.
How to go about this? Have a look in your nym-network-requester config directory:
How to go about this? Have a look in your `nym-network-requester` config directory:
```
# network requester binary
# nym-network-requester binary
ls -lt $HOME/.nym/service-providers/network-requester/*/data | grep "list"
# exit gateway binary
# nym-gateway binary
ls -lt $HOME/.nym/gateways/*/data/network-requester-data | grep "list"
# returns: allowed.list unknown.list
```
We already know that `allowed.list` is what lets requests go through. All unknown requests are logged to `unknown.list`. If you want to try using a new client type, just start the new application, point it at your local [socks client](https://nymtech.net/docs/clients/socks5-client.html) (configured to use your remote `nym-network-requester`), and keep copying URLs from `unknown.list` into `allowed.list` (it may take multiple tries until you get all of them, depending on the complexity of the application). Make sure to delete the copied ones in `unknown.list` and restart your exit gateway or standalone network requester.
We already know that `allowed.list` is what lets requests go through. All unknown requests are logged to `unknown.list`. If you want to try using a new client type, just start the new application, point it at your local [socks client](https://nymtech.net/docs/clients/socks5-client.html) (configured to use your remote `nym-network-requester`), and keep copying URLs from `unknown.list` into `allowed.list` (it may take multiple tries until you get all of them, depending on the complexity of the application). Make sure to delete the copied ones in `unknown.list` and restart your Exit Gateway or standalone Network Requester.
> If you are adding custom domains, please note that whilst they may appear in the logs of your network-requester as something like `api-0.core.keybaseapi.com:443`, you **only need** to include the main domain name, in this instance `keybaseapi.com`
### Running an open proxy
If you *really* want to run an open proxy, perhaps for testing purposes for your own use or among a small group of trusted friends, it is possible to do so. You can disable network checks by passing the flag `--open-proxy` flag when you run it. If you run in this configuration, you do so at your own risk.
If you *really* want to run an open proxy, perhaps for testing purposes for your own use or among a small group of trusted friends, it is possible to do so. You can disable Network checks by passing the flag `--open-proxy` flag when you run it. If you run in this configuration, you do so at your own risk.
## Testing your network requester
1. Make sure `nymtech.net` is in your `allowed.list` (remember to restart your network requester).
## Testing your Network Requester
1. Make sure `nymtech.net` is in your `allowed.list` (remember to restart your Network Requester).
2. Ensure that your network-requester is initialized and running.
2. Ensure that your `nym-network-requester` is initialized and running.
3. In another terminal window, run the following:
@@ -220,5 +220,5 @@ This command should return the following:
## Maintenance
For network requester upgrade (including an upgrade from `<v1.1.9` to `>= v1.1.10`), firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md).
For Network Requester upgrade (including an upgrade from `<v1.1.9` to `>= v1.1.10`), firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md).
@@ -3,7 +3,7 @@
To setup any type of Nym's node, start with building [Nym's platform](../binaries/building-nym.md) on the machine (VPS) where you want to run the node. Nodes will need to be bond to Nym's wallet, setup one [here](https://nymtech.net/docs/wallet/desktop-wallet.html).
This section contains setup guides for the following node types:
* [Mix node](./mix-node-setup.md)
* [Mix Node](./mix-node-setup.md)
* [Gateway](./gateway-setup.md)
* [Network Requester](./network-requester-setup.md)
* [Validator](./validator-setup.md)
@@ -78,7 +78,7 @@ Additional details can be obtained via various methods after you connect to your
##### Socket statistics with `ss`
```
sudo ss -s -t | grep 1789 # if you have specified a different port in your mix node config, change accordingly
sudo ss -s -t | grep 1789 # if you have specified a different port in your Mix Node config, change accordingly
```
This command should return a lot of data containing `ESTAB`. This command should work on every unix based system.
@@ -90,7 +90,7 @@ This command should return a lot of data containing `ESTAB`. This command should
lsof -v
# install if not installed
sudo apt install lsof
# run against mix node port
# run against nym-mix-node node port
sudo lsof -i TCP:1789 # if you have specified a different port in your mixnode config, change accordingly
```
@@ -110,7 +110,7 @@ nym-mixno 103349 root 57u IPv6 1333229976 0t0 TCP [2a03:b0c0:3:d0::ff3:
sudo journalctl -u nym-mixnode -o cat | grep "Since startup mixed"
```
If you have created `nym-mixnode.service` file (i.e. you are running your mix node via `systemd`) then this command shows you how many packets have you mixed so far, and should return a list of messages like this:
If you have created `nym-mixnode.service` file (i.e. you are running your Mix Node via `systemd`) then this command shows you how many packets have you mixed so far, and should return a list of messages like this:
```
2021-05-18T12:35:24.057Z INFO nym_mixnode::node::metrics > Since startup mixed 233639 packets!
@@ -140,7 +140,7 @@ For example `./target/debug/nym-network-requester --no-banner build-info --outpu
nmap -p 1789 <IP ADDRESS> -Pn
```
If your mix node is configured properly it should output something like this:
If your Mix Node is configured properly it should output something like this:
```
bob@desktop:~$ nmap -p 1789 95.296.134.220 -Pn
@@ -159,12 +159,12 @@ curl --location --request GET 'https://validator.nymtech.net/api/v1/mixnodes/'
Will return a list all nodes currently online.
You can query gateways by replacing `mixnodes` with `gateways` in the above command, and can query for the mixnodes and gateways on the Sandbox testnet by replacing `validator` with `sandbox-validator`.
You can query Gateways by replacing `nym-mixnodes` with `nym-gateways` in the above command, and can query for the Mix Nodes and Gateways on the Sandbox testnet by replacing `validator` with `sandbox-validator`.
#### Check with Network API
We currently have an API set up returning our metrics tests of the network. There are two endpoints to ping for information about your mix node, `report` and `history`. Find more information about this in the [Mixnodes metrics documentation](./maintenance.md#metrics--api-endpoints).
We currently have an API set up returning our metrics tests of the network. There are two endpoints to ping for information about your Mix Node, `report` and `history`. Find more information about this in the [Mixnodes metrics documentation](./maintenance.md#metrics--api-endpoints).
### Why is my node not mixing any packets?
@@ -172,24 +172,24 @@ If you are still unable to see your node on the dashboard, or your node is decla
- The firewall on your host machine is not configured properly. Checkout the [instructions](./maintenance.md#configure-your-firewall).
- You provided incorrect information when bonding your node.
- You are running your mix node from a VPS without IPv6 support.
- You did not use the `--announce-host` flag while running the mix node from your local machine behind NAT.
- You did not configure your router firewall while running the mix node from your local machine behind NAT, or you are lacking IPv6 support.
- Your mix node is not running at all, it has either exited / panicked or you closed the session without making the node persistent. Check out the [instructions](./maintenance.md#automating-your-node-with-tmux-and-systemd).
- You are running your Mix Node from a VPS without IPv6 support.
- You did not use the `--announce-host` flag while running the Mix Node from your local machine behind NAT.
- You did not configure your router firewall while running the Mix Node from your local machine behind NAT, or you are lacking IPv6 support.
- Your Mix Node is not running at all, it has either exited / panicked or you closed the session without making the node persistent. Check out the [instructions](./maintenance.md#automating-your-node-with-tmux-and-systemd).
```admonish caution
Your mix node **must speak both IPv4 and IPv6** in order to cooperate with other nodes and route traffic. This is a common reason behind many errors we are seeing among node operators, so check with your provider that your VPS is able to do this!
Your Mix Node **must speak both IPv4 and IPv6** in order to cooperate with other nodes and route traffic. This is a common reason behind many errors we are seeing among node operators, so check with your provider that your VPS is able to do this!
```
#### Incorrect bonding information
Check that you have provided the correct information when bonding your mix node in the web wallet interface. When in doubt, un-bond and then re-bond your node!
Check that you have provided the correct information when bonding your Mix Node in the web wallet interface. When in doubt, un-bond and then re-bond your node!
> All delegated stake will be lost when un-bonding! However the mix node must be operational in the first place for the delegation to have any effect.
> All delegated stake will be lost when un-bonding! However the Mix Node must be operational in the first place for the delegation to have any effect.
#### Missing `announce-host` flag
On certain cloud providers such as AWS and Google Cloud, you need to do some additional configuration of your firewall and use `--host` with your **local ip** and `--announce-host` with the **public ip** of your mix node host.
On certain cloud providers such as AWS and Google Cloud, you need to do some additional configuration of your firewall and use `--host` with your **local ip** and `--announce-host` with the **public ip** of your Mix Node host.
If the difference between the two is unclear, contact the help desk of your VPS provider.
@@ -222,15 +222,15 @@ bob@nym:~$ hostname -I
### Running on a local machine behind NAT with no fixed IP address
Your ISP has to be IPv6 ready if you want to run a mix node on your local machine. Sadly, in 2020, most of them are not and you won't get an IPv6 address by default from your ISP. Usually it is an extra paid service or they simply don't offer it.
Your ISP has to be IPv6 ready if you want to run a Mix Node on your local machine. Sadly, in 2020, most of them are not and you won't get an IPv6 address by default from your ISP. Usually it is an extra paid service or they simply don't offer it.
Before you begin, check if you have IPv6 [here](https://test-ipv6.cz/) or by running command explained in the [section above](./troubleshooting.md#no-ipv6-connectivity). If not, then don't waste your time to run a node which won't ever be able to mix any packet due to this limitation. Call your ISP and ask for IPv6, there is a plenty of it for everyone!
If all goes well and you have IPv6 available, then you will need to `init` the mix node with an extra flag, `--announce-host`. You will also need to edit your `config.toml` file each time your IPv4 address changes, that could be a few days or a few weeks. Check the your IPv4 in the [section above](./troubleshooting.md#no-ipv6-connectivity).
If all goes well and you have IPv6 available, then you will need to `init` the Mix Node with an extra flag, `--announce-host`. You will also need to edit your `config.toml` file each time your IPv4 address changes, that could be a few days or a few weeks. Check the your IPv4 in the [section above](./troubleshooting.md#no-ipv6-connectivity).
Additional configuration on your router might also be needed to allow traffic in and out to port 1789 and IPv6 support.
Here is a sample of the `init` command example to create the mix node config.
Here is a sample of the `init` command example to create the Mix Node config.
```
./nym-mixnode init --id <YOUR_ID> --host 0.0.0.0 --announce-host 85.160.12.13
@@ -244,7 +244,7 @@ Make sure you check if your node is really mixing. We are aiming to improve the
### Accidentally killing your node process on exiting session
When you close your current terminal session, you need to make sure you don't kill the mix node process! There are multiple ways on how to make it persistent even after exiting your ssh session, the easiest solution is to use `tmux` or `nohup`, and the more elegant solution is to run the node with `systemd`. Read the automation manual [here](./maintenance.md#automating-your-node-with-tmux-and-systemd).
When you close your current terminal session, you need to make sure you don't kill the Mix Node process! There are multiple ways on how to make it persistent even after exiting your ssh session, the easiest solution is to use `tmux` or `nohup`, and the more elegant solution is to run the node with `systemd`. Read the automation manual [here](./maintenance.md#automating-your-node-with-tmux-and-systemd).
### Common errors and warnings
@@ -266,14 +266,14 @@ Then you need to `--announce-host <PUBLIC_IP>` and `--host <LOCAL_IP>` on startu
Yes! Here is what you will need to do:
Assuming you would like to use port `1337` for your mix node, you need to open the new port (and close the old one):
Assuming you would like to use port `1337` for your Mix Node, you need to open the new port (and close the old one):
```
sudo ufw allow 1337
sudo ufw deny 1789
```
And then edit the mix node's config.
And then edit the Mix Node's config.
> If you want to change the port for an already running node, you need to stop the process before editing your config file.
@@ -287,25 +287,25 @@ nano ~/.nym/mixnodes/alice-node/config/config.toml
You will need to edit two parts of the file. `announce_address` and `listening_address` in the config.toml file. Simply replace `:1789` (the default port) with `:1337` (your new port) after your IP address.
Finally, restart your node. You should see if the mix node is using the port you have changed in the config.toml file right after you run the node.
Finally, restart your node. You should see if the Mix Node is using the port you have changed in the config.toml file right after you run the node.
### What is `verloc` and do I have to configure my mix node to implement it?
### What is `verloc` and do I have to configure my Mix Node to implement it?
`verloc` is short for _verifiable location_. Mixnodes and gateways now measure speed-of-light distances to each other, in an attempt to verify how far apart they are. In later releases, this will allow us to algorithmically verify node locations in a non-fake-able and trustworthy manner.
`verloc` is short for _verifiable location_. Mix Nodes and Gateways now measure speed-of-light distances to each other, in an attempt to verify how far apart they are. In later releases, this will allow us to algorithmically verify node locations in a non-fake-able and trustworthy manner.
You don't have to do any additional configuration for your node to implement this, it is a passive process that runs in the background of the mixnet from version `0.10.1` onward.
## Gateways & Network requesters
## Gateways & Network Requesters
### My gateway seems to be running but appears offline
### My Gateway seems to be running but appears offline
Check your [firewall](./maintenance.md#configure-your-firewall) is active and if the necessary ports are open / allowed.
### My exit gateway "is still not online..."
### My exit Gateway "is still not online..."
The Nyx chain epoch takes up to 60 min. To prevent the gateway getting blacklisted, it's important to run it right after the bonding process to return positive response our API testing it's routing score.
The Nyx chain epoch takes up to 60 min. To prevent the Gateway getting blacklisted, it's important to run it before and during the bonding process. In case it already got blacklisted run it for at several hours. During this time your node is tested by `nym-api` and every positive response picks up your Gateway's routing score.
You may want to disconnect the network requester and let it run as a gatewy alone for some time to regain better routing score and then areturn to the full [exit gateway finctionality](./gateway-setup.md#initialising-gateway-with-network-requester).
You may want to disconnect the Network Requester and let it run as a Gateway alone for some time to regain better routing score and then return to the full [Exit Gateway finctionality](./gateway-setup.md#initialising-gateway-with-network-requester).
## Validators
@@ -1,12 +1,12 @@
# Validators
> Nym has two main codebases:
> Nym has two main codebases:
> - the [Nym platform](https://github.com/nymtech/nym), written in Rust. This contains all of our code except for the validators.
> - the [Nym validators](https://github.com/nymtech/nyxd), written in Go.
> - the [Nym validators](https://github.com/nymtech/nyxd), written in Go & maintained as a no-modification fork of [wasmd](https://github.com/CosmWasm/wasmd)
The validator is built using [Cosmos SDK](https://cosmos.network) and [Tendermint](https://tendermint.com), with a [CosmWasm](https://cosmwasm.com) smart contract controlling the directory service, node bonding, and delegated mixnet staking.
The validator is a Go application which implements it's functionalities using [Cosmos SDK](https://v1.cosmos.network/sdk). The underlying state-replication engine is powered by [CometBFT](https://cometbft.com/), where the consensus mechanism is based on the [Tendermint Consensus Algorithm](https://arxiv.org/abs/1807.04938). Finally, a [CosmWasm](https://cosmwasm.com) smart contract module controls crucial mixnet functionalities like decentralised directory service, node bonding, and delegated mixnet staking.
> We are currently working towards building up a closed set of reputable validators. You can ask us for coins to get in, but please don't be offended if we say no - validators are part of our system's core security and we are starting out with people we already know or who have a solid reputation.
> We are currently running mainnet with a closed set of reputable validators. To ensure decentralisation of Nyx chain, we are working on a mechanism to onboard new validators to the network. To join the waitlist, please drop an email to `validators [at] nymtech.net` with details of your setup, experience and any other relevent information
## Building your validator
@@ -33,9 +33,9 @@ pacman -S git gcc jq
`Go` can be installed via the following commands (taken from the [Go Download and install page](https://go.dev/doc/install)):
```
# First remove any existing old Go installation and extract the archive you just downloaded into /usr/local:
# First remove any existing old Go installation and extract the archive you just downloaded into /usr/local:
# You may need to run the command as root or through sudo
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.6.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.10.linux-amd64.tar.gz
# Add /usr/local/go/bin to the PATH environment variable
export PATH=$PATH:/usr/local/go/bin
@@ -47,16 +47,12 @@ Verify `Go` is installed with:
```
go version
# Should return something like:
go version go1.20.4 linux/amd64
go version go1.20.10 linux/amd64
```
### Download a precompiled validator binary
You can find pre-compiled binaries for Ubuntu `22.04` and `20.04` [here](https://github.com/nymtech/nyxd/releases).
```admonish caution title=""
There are seperate releases for Mainnet and the Sandbox testnet - make sure to download the correct binary to avoid `bech32Prefix` mismatches.
```
### Manually compiling your validator binary
The codebase for the Nyx validators can be found [here](https://github.com/nymtech/nyxd).
@@ -64,15 +60,13 @@ The validator binary can be compiled by running the following commands:
```
git clone https://github.com/nymtech/nyxd.git
cd nyxd
# Make sure to check releases for the latest version information
git checkout release/<NYXD_VERSION>
# Mainnet
# Build the binaries
make build
# Sandbox testnet
BECH32_PREFIX=nymt make build
```
At this point, you will have a copy of the `nyxd` binary in your `build/` directory. Test that it's compiled properly by running:
```
@@ -83,50 +77,49 @@ You should see a similar help menu printed to you:
~~~admonish example collapsible=true title="Console output"
```
Wasm Daemon (server)
Nyx Daemon (server)
Usage:
nyxd [command]
Available Commands:
add-genesis-account Add a genesis account to genesis.json
add-wasm-genesis-message Wasm genesis subcommands
collect-gentxs Collect genesis txs and output a genesis.json file
config Create or query an application CLI configuration file
debug Tool for helping with debugging your application
export Export state to JSON
gentx Generate a genesis tx carrying a self delegation
help Help about any command
init Initialize private validator, p2p, genesis, and application configuration files
keys Manage your application's keys
query Querying subcommands
rollback rollback cosmos-sdk and tendermint state by one height
start Run the full node
status Query remote node for status
tendermint Tendermint subcommands
tx Transactions subcommands
validate-genesis validates the genesis file at the default location or at the location passed as an arg
version Print the application binary version information
completion Generate the autocompletion script for the specified shell
config Create or query an application CLI configuration file
debug Tool for helping with debugging your application
export Export state to JSON
genesis Application's genesis-related subcommands
help Help about any command
init Initialize private validator, p2p, genesis, and application configuration files
keys Manage your application's keys
prune Prune app history states by keeping the recent heights and deleting old heights
query Querying subcommands
rollback rollback cosmos-sdk and tendermint state by one height
rosetta spin up a rosetta server
start Run the full node
status Query remote node for status
tendermint Tendermint subcommands
testnet subcommands for starting or configuring local testnets
tx Transactions subcommands
version Print the application binary version information
Flags:
-h, --help help for nyxd
--home string directory for config and data (default "/home/willow/.nyxd")
--home string directory for config and data
--log_format string The logging format (json|plain) (default "plain")
--log_level string The logging level (trace|debug|info|warn|error|fatal|panic) (default "info")
--trace print out full stack trace on errors
Use "nyxd [command] --help" for more information about a command.
```
~~~
### Linking `nyxd` to `libwasmvm.so`
`libwasmvm.so` is the wasm virtual machine which is needed to execute smart contracts in `v0.26.1`. This file is renamed in `libwasmvm.x86_64.so` in `v0.31.1`.
`libwasmvm.so` is the wasm virtual machine which is needed to execute smart contracts in `v0.26.1`. This file is renamed in `libwasmvm.x86_64.so` in `v0.31.1` and above.
If you downloaded your `nyxd` binary from Github, you will have seen this file when un-`tar`-ing the `.tar.gz` file from the releases page.
If you are seeing an error concerning this file when trying to run `nyxd`, then you need to move the `libwasmvm.so` file to correct location.
If you are seeing an error concerning this file when trying to run `nyxd`, then you need to move the `libwasmvm.x86_64.so` file to correct location.
Simply `cp` or `mv` that file to `/lib/x86_64-linux-gnu/` and re-run `nyxd`.
@@ -152,40 +145,39 @@ This should return the regular help menu:
~~~admonish example collapsible=true title="Console output"
```
Wasm Daemon (server)
Nyx Daemon (server)
Usage:
nyxd [command]
Available Commands:
add-genesis-account Add a genesis account to genesis.json
add-wasm-genesis-message Wasm genesis subcommands
collect-gentxs Collect genesis txs and output a genesis.json file
config Create or query an application CLI configuration file
debug Tool for helping with debugging your application
export Export state to JSON
gentx Generate a genesis tx carrying a self delegation
help Help about any command
init Initialize private validator, p2p, genesis, and application configuration files
keys Manage your application's keys
query Querying subcommands
rollback rollback cosmos-sdk and tendermint state by one height
start Run the full node
status Query remote node for status
tendermint Tendermint subcommands
tx Transactions subcommands
validate-genesis validates the genesis file at the default location or at the location passed as an arg
version Print the application binary version information
completion Generate the autocompletion script for the specified shell
config Create or query an application CLI configuration file
debug Tool for helping with debugging your application
export Export state to JSON
genesis Application's genesis-related subcommands
help Help about any command
init Initialize private validator, p2p, genesis, and application configuration files
keys Manage your application's keys
prune Prune app history states by keeping the recent heights and deleting old heights
query Querying subcommands
rollback rollback cosmos-sdk and tendermint state by one height
rosetta spin up a rosetta server
start Run the full node
status Query remote node for status
tendermint Tendermint subcommands
testnet subcommands for starting or configuring local testnets
tx Transactions subcommands
version Print the application binary version information
Flags:
-h, --help help for nyxd
--home string directory for config and data (default "/home/willow/.nyxd")
--home string directory for config and data
--log_format string The logging format (json|plain) (default "plain")
--log_level string The logging level (trace|debug|info|warn|error|fatal|panic) (default "info")
--trace print out full stack trace on errors
Use "nyxd [command] --help" for more information about a command.
```
~~~
@@ -224,7 +216,7 @@ You can use the following command to download them for the correct network:
wget -O $HOME/.nyxd/config/genesis.json https://nymtech.net/genesis/genesis.json
# Sandbox testnet
wget -O $HOME/.nyxd/config/genesis.json https://sandbox-validator1.nymtech.net/snapshots/genesis.json
wget -O $HOME/.nyxd/config/genesis.json https://sandbox-validator1.nymtech.net/snapshots/genesis.json | jq '.result.genesis'
```
### `config.toml` configuration
@@ -234,21 +226,18 @@ Edit the following config options in `$HOME/.nyxd/config/config.toml` to match t
```
# Mainnet
persistent_peers = "ee03a6777fb76a2efd0106c3769daaa064a3fcb5@51.79.21.187:26656"
create_empty_blocks = false
laddr = "tcp://0.0.0.0:26656"
```
```
# Sandbox testnet
cors_allowed_origins = ["*"]
persistent_peers = "8421c0a3d90d490e27e8061f2abcb1276c8358b6@sandbox-validator1.nymtech.net:26666"
create_empty_blocks = false
persistent_peers = "26f7782aff699457c8e6dd9a845e5054c9b0707e@sandbox-validator1.nymtech.net:26666"
laddr = "tcp://0.0.0.0:26656"
```
These affect the following:
These affect the following:
* `persistent_peers = "<PEER_ADDRESS>@<DOMAIN>.nymtech.net:26666"` allows your validator to start pulling blocks from other validators. **The main sandbox validator listens on `26666` instead of the default `26656` for debugging**. It is recommended you do not change your port from `26656`.
* `create_empty_blocks = false` will save space
* `laddr = "tcp://0.0.0.0:26656"` is in your p2p configuration options
Optionally, if you want to enable [Prometheus](https://prometheus.io/) metrics then the following must also match in the `config.toml`:
@@ -270,11 +259,11 @@ In the file `$HOME/nyxd/config/app.toml`, set the following values:
```
# Mainnet
minimum-gas-prices = "0.025unym,0.025unyx"
enable = true in the `[api]` section to get the API server running
```
```
# Sandbox Testnet
minimum-gas-prices = "0.025unymt,0.025unyxt"
minimum-gas-prices = "0.025unym,0.025unyx"
enable = true` in the `[api]` section to get the API server running
```
@@ -285,9 +274,13 @@ You'll need an admin account to be in charge of your validator. Set that up with
nyxd keys add nyxd-admin
```
This will add keys for your administrator account to your system's keychain and log your name, address, public key, and mnemonic. As the instructions say, remember to **write down your mnemonic**.
```admonish tip title="Key Backends"
Cosmos SDK offers multiple backends for securing your keys. Please refer to the Cosmos SDK [docs on available keyring backends](https://docs.cosmos.network/main/user/run-node/keyring#available-backends-for-the-keyring) to learn more
```
You can get the admin account's address with:
While using the default settings, this will add keys for your account to your system's keychain and log your name, address, public key, and mnemonic. As the instructions say, remember to **write down your mnemonic**.
You can get the current account address with:
```
nyxd keys show nyxd-admin -a
@@ -297,10 +290,6 @@ Type in your keychain **password**, not the mnemonic, when asked.
## Starting your validator
```admonish caution title=""
If you are running a Sandbox testnet validator, please skip the `validate-genesis` command: it will fail due to the size of the genesis file as this is a fork of an existing chain state.
```
Everything should now be ready to go. You've got the validator set up, all changes made in `config.toml` and `app.toml`, the Nym genesis file copied into place (replacing the initial auto-generated one). Now let's validate the whole setup:
```
@@ -313,7 +302,7 @@ If this check passes, you should receive the following output:
File at /path/to/genesis.json is a valid genesis file
```
> If this test did not pass, check that you have replaced the contents of `/<PATH-TO>/.nymd/config/genesis.json` with that of the correct genesis file.
> If this test did not pass, check that you have replaced the contents of `/<PATH-TO>/.nyxd/config/genesis.json` with that of the correct genesis file.
### Open firewall ports
@@ -323,14 +312,18 @@ Before starting the validator, we will need to open the firewall ports:
# if ufw is not already installed:
sudo apt install ufw
sudo ufw enable
sudo ufw allow 1317,26656,26660,22,80,443/tcp
# Customise according to your port bindings. This is only for reference
# 26656 : p2p gossip port
# 26660: If prometheus is enabled
# 22 : Default SSH port
sudo ufw allow 26656,26660,22
# to check everything worked
sudo ufw status
```
Ports `22`, `80`, and `443` are for ssh, http, and https connections respectively. The rest of the ports are documented [here](https://docs.cosmos.network/main/core/grpc_rest).
For more information about your validator's port configuration, check the [validator port reference table](./maintenance.md#ports) below.
For more information about your validator's port configuration, check the [validator port reference table](./maintenance.md#ports) below. These can be customised in `app.toml` and `config.toml` files.
> If you are planning to use [Cockpit](https://cockpit-project.org/) on your validator server then you will have defined a different `grpc` port in your `config.toml` above: remember to open this port as well.
@@ -376,8 +369,7 @@ Once your validator has synced and you have received tokens, you can join consen
```
# Mainnet
nyxd tx staking create-validator
--amount=10000000unyx
--fees=0unyx
--amount=<10000000unyx>
--pubkey=$(/home/<USER>/<PATH-TO>/nyxd/binaries/nyxd tendermint show-validator)
--moniker="<YOUR_VALIDATOR_NAME>"
--chain-id=nyx
@@ -387,14 +379,14 @@ nyxd tx staking create-validator
--min-self-delegation="1"
--gas="auto"
--gas-adjustment=1.15
--from="KEYRING_NAME"
--node https://rpc-1.nyx.nodes.guru:443
--gas-prices=0.025unyx
--from=<"KEYRING_NAME">
--node=https://rpc.nymtech.net:443
```
```
# Sandbox Testnet
nyxd tx staking create-validator
--amount=10000000unyxt
--fees=5000unyxt
--amount=<10000000unyx>
--pubkey=$(/home/<USER>/<PATH-TO>/nym/binaries/nyxd tendermint show-validator)
--moniker="<YOUR_VALIDATOR_NAME>"
--chain-id=sandbox
@@ -404,13 +396,13 @@ nyxd tx staking create-validator
--min-self-delegation="1"
--gas="auto"
--gas-adjustment=1.15
--from="KEYRING_NAME"
--gas-prices=0.025unyx
--from=<"KEYRING_NAME">
--node https://sandbox-validator1.nymtech.net:443
```
You'll need either `unyxt` tokens on Sandbox, or `unyx` tokens on mainnet to perform this command.
You'll need Nyx tokens on mainnet / sandbox to perform the above tasks.
> We are currently working towards building up a closed set of reputable validators. You can ask us for coins to get in, but please don't be offended if we say no - validators are part of our system's core security and we are starting out with people we already know or who have a solid reputation.
If you want to edit some details for your node you will use a command like this:
@@ -424,8 +416,8 @@ nyxd tx staking edit-validator
--identity="<YOUR_IDENTITY>"
--gas="auto"
--gas-adjustment=1.15
--gas-prices=0.025unyx
--from="KEYRING_NAME"
--fees 2000unyx
```
```
# Sandbox testnet
@@ -437,8 +429,8 @@ nyxd tx staking edit-validator
--identity="<YOUR_IDENTITY>"
--gas="auto"
--gas-adjustment=1.15
--gas-prices=0.025unyx
--from="KEYRING_NAME"
--fees 2000unyxt
```
With above command you can specify the `gpg` key last numbers (as used in `keybase`) as well as validator details and your email for security contact.
@@ -446,9 +438,6 @@ With above command you can specify the `gpg` key last numbers (as used in `keyba
### Automating your validator with systemd
You will most likely want to automate your validator restarting if your server reboots. Checkout the [maintenance page](./maintenance.md#systemd) with a quick tutorial.
### Installing and configuring nginx for HTTPS
If you want to set up a reverse proxying on the validator server to improve security and performance, using [nginx](https://www.nginx.com/resources/glossary/nginx/#:~:text=NGINX%20is%20open%20source%20software,%2C%20media%20streaming%2C%20and%20more.&text=In%20addition%20to%20its%20HTTP,%2C%20TCP%2C%20and%20UDP%20servers.), follow the manual on the [maintenance page](./maintenance.md#setup).
### Setting the ulimit
@@ -466,7 +455,7 @@ nyxd tx slashing unjail
--chain-id=nyx
--gas=auto
--gas-adjustment=1.4
--fees=7000unyx
--gas-prices=0.025unyx
```
```
# Sandbox Testnet
@@ -476,7 +465,7 @@ nyxd tx slashing unjail
--chain-id=sandbox
--gas=auto
--gas-adjustment=1.4
--fees=7000unyxt
--gas-prices=0.025unyx
```
### Upgrading your validator
@@ -496,7 +485,7 @@ If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog
You can check your current balances with:
```
nymd query bank balances ${ADDRESS}
nyxd query bank balances ${ADDRESS}
```
For example, on the Sandbox testnet this would return:
@@ -504,7 +493,7 @@ For example, on the Sandbox testnet this would return:
```yaml
balances:
- amount: "919376"
denom: unymt
denom: unym
pagination:
next_key: null
total: "0"
@@ -516,22 +505,21 @@ You can, of course, stake back the available balance to your validator with the
```
# Mainnet
nyxd tx staking delegate VALOPERADDRESS AMOUNTunym
nyxd tx staking delegate VALOPERADDRESS AMOUNTunyx
--from="KEYRING_NAME"
--keyring-backend=os
--chain-id=nyx
--gas="auto"
--gas-adjustment=1.15
--fees 5000unyx
```
```
--gas-prices=0.025unyx
# Sandbox Testnet
nyxd tx staking delegate VALOPERADDRESS AMOUNTunymt
nyxd tx staking delegate VALOPERADDRESS AMOUNTunyx
--from="KEYRING_NAME"
--keyring-backend=os
--chain-id=sandbox
--gas="auto"
--gas-adjustment=1.15
--fees 5000unyxt
--gas-prices=0.025unyx
```
+8 -2
View File
@@ -1,8 +1,11 @@
import * as React from 'react';
import React from 'react';
import Box from '@mui/material/Box';
import { Typography } from '@mui/material';
import MuiLink from '@mui/material/Link';
import { Link } from 'react-router-dom';
import Typography from '@mui/material/Typography';
import { Socials } from './Socials';
import { useIsMobile } from '../hooks/useIsMobile';
import { NymVpnIcon } from '../icons/NymVpn';
export const Footer: FCWithChildren = () => {
const isMobile = useIsMobile();
@@ -31,6 +34,9 @@ export const Footer: FCWithChildren = () => {
mb: 2,
}}
>
<MuiLink component={Link} to="http://nymvpn.com" target="_blank" underline="none" marginRight={1}>
<NymVpnIcon />
</MuiLink>
<Socials isFooter />
</Box>
)}
+4
View File
@@ -22,6 +22,7 @@ import { useMainContext } from '../context/main';
import { MobileDrawerClose } from '../icons/MobileDrawerClose';
import { Socials } from './Socials';
import { Footer } from './Footer';
import { NymVpnIcon } from '../icons/NymVpn';
import { DarkLightSwitchDesktop } from './Switch';
import { NavOptionType } from '../context/nav';
@@ -341,6 +342,9 @@ export const Nav: FCWithChildren = ({ children }) => {
alignItems: 'center',
}}
>
<MuiLink component={Link} to="http://nymvpn.com" target="_blank" underline="none" marginRight={1}>
<NymVpnIcon />
</MuiLink>
<Socials />
<DarkLightSwitchDesktop defaultChecked />
</Box>
+56
View File
@@ -0,0 +1,56 @@
import * as React from 'react';
interface DiscordIconProps {
size?: { width: number; height: number };
}
export const NymVpnIcon: FCWithChildren<DiscordIconProps> = ({ size }) => (
<svg width={size?.width} height={size?.height} viewBox="0 0 170 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M19.6118 0.128906H19.5405V0.187854V20.7961L10.7849 0.164277L10.773 0.128906H10.7255H5.75959H0.187819H0.128418V0.187854V23.8142V23.8732H0.187819H5.75959H5.81899V23.8142V3.17063L14.6103 23.8378L14.6222 23.8732H14.6697H19.6118H25.1717H25.2311V23.8142V0.187854V0.128906H25.1717H19.6118Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19.4121 0H25.3603V24H14.5297L14.4901 23.8819L5.94824 3.80121V24H0V0H10.8663L10.906 0.118132L19.4121 20.1621V0ZM19.5409 20.7951L10.7853 0.163225L10.7734 0.127854H0.128835V23.8721H5.81941V3.16958L14.6107 23.8368L14.6226 23.8721H25.2315V0.127854H19.5409V20.7951Z"
fill="white"
/>
<path
d="M89.8116 0.128906H79.1908H79.1314L79.1195 0.176068L73.6784 20.8904L68.2255 0.176068L68.2136 0.128906H68.1661H57.5215H57.4502V0.187854V23.8142V23.8732H57.5215H63.0814H63.1408V23.8142V3.33568L68.5225 23.826L68.5343 23.8732H68.5937H78.7394H78.7869L78.7988 23.826L84.1804 3.33568V23.8142V23.8732H84.2398H89.8116H89.871V23.8142V0.187854V0.128906H89.8116Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M79.0312 0H90.0003V24H84.052V4.33208L78.9242 23.856L78.9238 23.8572L78.8879 24H68.4342L68.3982 23.8572L68.3979 23.856L63.27 4.33208V24H57.3218V0H68.3146L68.3505 0.142699L68.3509 0.144015L73.6787 20.383L78.9949 0.144015L78.9953 0.142765L79.0312 0ZM73.6788 20.8894L68.2259 0.175015L68.214 0.127854H57.4506V23.8721H63.1412V3.33463L68.5229 23.825L68.5348 23.8721H78.7873L78.7992 23.825L84.1809 3.33463V23.8721H89.8714V0.127854H79.1318L79.1199 0.175015L73.6788 20.8894Z"
fill="white"
/>
<path
d="M48.2909 0.128906H48.2553L48.2434 0.152487L41.4836 11.8124L34.6882 0.152487L34.6763 0.128906H34.6407H28.2135H28.0947L28.1541 0.223225L38.6205 18.2142V23.8142V23.8732H38.6799H44.2517H44.3111V23.8142V18.2142L54.7775 0.223225L54.8369 0.128906H54.7181H48.2909Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M48.1757 0H55.0693L54.8879 0.288036L44.4399 18.2474V24H38.4917V18.2474L28.0437 0.288036L27.8623 0H34.756L34.8017 0.0907854L41.4833 11.5555L48.1299 0.0909153L48.1757 0ZM48.2434 0.151434L41.4836 11.8114L34.6882 0.151434L34.6763 0.127854H28.0948L28.1542 0.222173L38.6205 18.2131V23.8721H44.3111V18.2131L54.7775 0.222173L54.8369 0.127854H48.2553L48.2434 0.151434Z"
fill="white"
/>
<path
d="M169.238 0V24H166.422C166.006 24 165.654 23.9341 165.366 23.8023C165.088 23.6596 164.811 23.418 164.534 23.0776L153.542 8.76321C153.584 9.19149 153.611 9.60878 153.622 10.0151C153.643 10.4104 153.654 10.7838 153.654 11.1352V24H148.886V0H151.734C151.968 0 152.166 0.0109813 152.326 0.032944C152.486 0.0549066 152.63 0.0988326 152.758 0.164722C152.886 0.219629 153.008 0.30199 153.126 0.411805C153.243 0.521619 153.376 0.669869 153.526 0.856553L164.614 15.2697C164.56 14.8085 164.523 14.3638 164.502 13.9355C164.48 13.4962 164.47 13.0844 164.47 12.7001V0H169.238Z"
fill="#A8A6A6"
/>
<path
d="M134.206 11.7776C135.614 11.7776 136.627 11.4317 137.246 10.7399C137.865 10.048 138.174 9.08167 138.174 7.84077C138.174 7.29169 138.094 6.79204 137.934 6.3418C137.774 5.89156 137.529 5.50721 137.198 5.18874C136.878 4.8593 136.467 4.60673 135.966 4.43102C135.475 4.25532 134.889 4.16747 134.206 4.16747H131.39V11.7776H134.206ZM134.206 0C135.849 0 137.257 0.203157 138.43 0.609471C139.614 1.0048 140.585 1.55388 141.342 2.25669C142.11 2.95951 142.675 3.78861 143.038 4.74399C143.401 5.69938 143.582 6.73164 143.582 7.84077C143.582 9.03775 143.395 10.1359 143.022 11.1352C142.649 12.1345 142.078 12.9911 141.31 13.7049C140.542 14.4187 139.566 14.9787 138.382 15.385C137.209 15.7804 135.817 15.978 134.206 15.978H131.39V24H125.982V0H134.206Z"
fill="#A8A6A6"
/>
<path
d="M121.584 0L112.24 24H107.344L98 0H102.352C102.821 0 103.2 0.115305 103.488 0.345915C103.776 0.565545 103.995 0.851064 104.144 1.20247L108.656 14.0508C108.869 14.6108 109.077 15.2258 109.28 15.8957C109.483 16.5546 109.675 17.2464 109.856 17.9712C110.005 17.2464 110.171 16.5546 110.352 15.8957C110.544 15.2258 110.747 14.6108 110.96 14.0508L115.44 1.20247C115.557 0.894989 115.765 0.620452 116.064 0.378861C116.373 0.126287 116.752 0 117.2 0H121.584Z"
fill="#A8A6A6"
/>
</svg>
);
NymVpnIcon.defaultProps = {
size: { width: 80, height: 12 },
};
+5
View File
@@ -81,3 +81,8 @@ cpucycles = [
"opentelemetry",
"nym-bin-common/tracing",
]
[package.metadata.deb]
name = "nym-mixnode"
maintainer-scripts = "debian"
systemd-units = { enable = false }
+6
View File
@@ -0,0 +1,6 @@
#DEBHELPER#
useradd nym
mkdir -p /etc/nym
chown -R nym /etc/nym
su nym -c 'NYM_HOME_DIR=/etc/nym nym-mixnode init --host 0.0.0.0 --id nym-mixnode'
+11
View File
@@ -0,0 +1,11 @@
[Unit]
Description=Nym Mixnode
After=network-online.target
[Service]
ExecStart=/usr/bin/nym-mixnode run --id nym-mixnode
User=nym
Environment="NYM_HOME_DIR=/etc/nym"
[Install]
WantedBy=multi-user.target
+23 -11
View File
@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"axios": "^0.27.2",
"axios": "^1.6.0",
"eslint": "^8.21.0",
"form-data": "4.0.0",
"json-stringify-safe": "5.0.1",
@@ -1671,12 +1671,13 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
"dependencies": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios-mock-adapter": {
@@ -4186,6 +4187,11 @@
"node": ">= 6"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -6184,12 +6190,13 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
"requires": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"axios-mock-adapter": {
@@ -8023,6 +8030,11 @@
"sisteransi": "^1.0.5"
}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+1 -1
View File
@@ -24,7 +24,7 @@
"npm": "8.x"
},
"dependencies": {
"axios": "^0.27.2",
"axios": "^1.6.0",
"eslint": "^8.21.0",
"form-data": "4.0.0",
"json-stringify-safe": "5.0.1",
+15 -9
View File
@@ -981,13 +981,14 @@ axios-mock-adapter@^1.20.0:
fast-deep-equal "^3.1.3"
is-buffer "^2.0.5"
axios@^0.27.2:
version "0.27.2"
resolved "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz"
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
axios@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102"
integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==
dependencies:
follow-redirects "^1.14.9"
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
babel-jest@^28.1.3:
version "28.1.3"
@@ -1566,10 +1567,10 @@ flatted@^3.1.0:
resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz"
integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==
follow-redirects@^1.14.9:
version "1.15.1"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz"
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
form-data@4.0.0, form-data@^4.0.0:
version "4.0.0"
@@ -2575,6 +2576,11 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "extension-storage"
version = "1.2.4-rc.1"
version = "1.2.4-rc.2"
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/nymtech/nym"
+4
View File
@@ -0,0 +1,4 @@
build/*
data/*
dist/*
target/*
+32
View File
@@ -0,0 +1,32 @@
# Generated by Cargo
# will have compiled files and executables
target
# These are backup files generated by rustfmt
**/*.rs.bk
node_modules
data
build
.cargo
.DS_Store
# generated Typescript types from Rust
bindings
staging-config
dist
.nymvpn
windows/x86_64*
*.wixobj
*.wixpdb
*.msi
mkcert*
nymvpn-oss-licenses.html
nymvpn-oss-licenses-rust.html
third-party-licenses.txt
+3
View File
@@ -0,0 +1,3 @@
{
"svg.preview.background": "editor"
}
+121
View File
@@ -0,0 +1,121 @@
# Building nymvpn app
## Install build dependencies
### Common for all Platforms
```
cargo install cargo-deb
cargo install cargo-generate-rpm
cargo install --force cargo-make
cargo install sd
cargo install ripgrep
cargo install cargo-about
```
### Linux
```
apt install build-essential \
pkg-config \
libgtk-3-dev \
libssl-dev \
libsoup2.4-dev \
libjavascriptcoregtk-4.0-dev \
libwebkit2gtk-4.0-dev \
libmnl-dev \
libnftnl-dev \
protobuf-compiler \
zip \
```
Install protoc on x86_64/amd64 machines
```
# x86_64
curl -Lo protoc-3.19.1-linux-x86_64.zip \
https://github.com/protocolbuffers/protobuf/releases/download/v3.19.1/protoc-3.19.1-linux-x86_64.zip && \
unzip protoc-3.19.1-linux-x86_64.zip -d /tmp && \
mv /tmp/bin/protoc /usr/bin/protoc && \
rm protoc-3.19.1-linux-x86_64.zip
```
Install protoc on arm64 machines
```
# aarch64
curl -Lo protoc-3.19.1-linux-aarch_64.zip \
https://github.com/protocolbuffers/protobuf/releases/download/v3.19.1/protoc-3.19.1-linux-aarch_64.zip && \
unzip protoc-3.19.1-linux-aarch_64.zip -d /tmp && \
sudo mv /tmp/bin/protoc /usr/bin/protoc && \
rm protoc-3.19.1-linux-aarch_64.zip
```
### macOS
TODO
### Windows
TODO
## Build Debian package
```
cargo make deb
```
## Build RPM package
```
cargo make rpm
```
## Build macOS package
```
cargo make pkg
```
To codesign for distribution provide following environment variables:
```
APPLE_TEAM_ID=...
APPLICATION_SIGNING_IDENTITY=...
INSTALLER_SIGNING_IDENTITY=...
cargo make pkg
```
## Build installer for Windows
```
cargo make msi
```
To codesign for distribution:
```
SIGN=true cargo make msi
```
## Building for Production for Linux
### Build the Builder
Build the Docker image to build nymvpn app.
```
cd nymvpn-packages
cargo make builder
```
This will output `tag.txt`, commit it into source code.
### Build .deb and .rpm
This step uses builder Docker image with tag in `nymvpn-packages/tag.txt`. The final rpm and deb packages will be stored in `dist` directory.
```
# For host platform
cargo make linux
# For target platform
cargo make -e TARGET=aarch64-unknown-linux-gnu linux
```
+7
View File
@@ -0,0 +1,7 @@
# Contributing
Firstly, thank you for considering to contribute and make nymvpn-app better! Please note that all of your contributions would be under GPL-3.0.
- Please see issues with `help wanted` or `good first issue` labels.
- For bug fixes, Pull Requests or Github Issues are welcome.
- For new features its best to create a Github Issue with detailed description.
+8625
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
[workspace]
resolver = "2"
members = [
"nymvpn-cli",
"nymvpn-config",
"nymvpn-controller",
"nymvpn-daemon",
"nymvpn-entity",
"nymvpn-migration",
"nymvpn-packages",
"nymvpn-server",
"nymvpn-types",
"nymvpn-ui/src-tauri",
]
[profile.release]
#opt-level = 'z' # Optimize for size.
opt-level = 3
lto = true # Enable Link Time Optimization
#codegen-units = 1 # Reduce number of codegen units to increase optimizations.
#panic = 'abort' # Abort on panic
strip = true # Strip symbols from binary*
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+331
View File
@@ -0,0 +1,331 @@
[env]
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
PWD = {script = ["pwd"]}
REPO_ROOT = "${PWD}"
BUILDER_TAG = {script = ["cat nymvpn-packages/tag.txt"] }
UPSTREAM_REPO = "https://github.com/upvpn/mullvadvpn-app.git"
UPSTREAM_REV = "2023.3.upvpn"
UPSTREAM_REPO_PATH = "${PWD}/.upvpn/mullvadvpn-app"
RUSTUP_HOST = { script = ["rustup show | rg host | awk '{print $3}'"] }
APP_VERSION = { script = ["grep version nymvpn-packages/Cargo.toml | cut -d '\"' -f2"]}
TARGET = { script = ["echo ${RUSTUP_HOST}"], condition = { env_not_set = ["TARGET"] } }
STRIP = {script = ["([ ${TARGET} = \"aarch64-unknown-linux-gnu\" ] && echo \"--no-strip\") || echo \"--strip\""]}
[tasks.release-build]
workspace = false
command = "cargo"
args = ["build", "--release", "--target", "${TARGET}"]
[tasks.deb]
workspace = false
command = "cargo"
args = ["deb", "-p", "nymvpn-packages", "--target", "${TARGET}", "${STRIP}", "${@}"]
dependencies = ["oss", "ui", "wglib"]
[tasks.rpm]
workspace = false
command = "cargo"
args = ["generate-rpm", "-p", "nymvpn-packages", "--target", "${TARGET}", "${@}"]
dependencies = ["oss", "ui", "wglib", "release-build", "strip"]
[tasks.rpm-no-build]
workspace = false
command = "cargo"
args = ["generate-rpm", "-p", "nymvpn-packages", "--target", "${TARGET}", "${@}"]
dependencies = ["ui", "wglib", "strip"]
[tasks.strip]
workspace = false
script = '''
CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-target}
if [ "${STRIP:-}" = "--strip" ]; then
for file in nymvpn nymvpn-ui nymvpn-daemon
do
output="${CARGO_TARGET_DIR}/${TARGET}/release/${file}"
if [ -f "${output}" ]; then
strip "${output}"
fi
done
fi
'''
[tasks.pkg]
workspace = false
script = '''
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')
cp -R nymvpn-packages/macos ${tempdir}
cp nymvpn-assets/icons/icon.icns ${tempdir}/macos/pkg/root/Applications/upvpn.app/Contents/Resources
cp nymvpn-packages/nymvpn-oss-licenses.html ${tempdir}/macos/pkg/root/Applications/upvpn.app/Contents/Resources
cp target/${TARGET}/release/nymvpn ${tempdir}/macos/pkg/root/Applications/upvpn.app/Contents/Resources
cp target/${TARGET}/release/nymvpn-daemon ${tempdir}/macos/pkg/root/Applications/upvpn.app/Contents/Resources
cp target/${TARGET}/release/nymvpn-ui ${tempdir}/macos/pkg/root/Applications/upvpn.app/Contents/MacOS
rm ${tempdir}/macos/pkg/root/Applications/upvpn.app/Contents/MacOS/.gitkeep
pushd ${tempdir}/macos
./build.sh "${APP_VERSION}"
popd
mkdir -p target/pkg
AARCH=$(uname -m)
cp ${tempdir}/macos/nymvpn-${APP_VERSION}.pkg target/pkg/nymvpn-${APP_VERSION}-${AARCH}.pkg
echo "Output: target/pkg/nymvpn-${APP_VERSION}-${AARCH}.pkg"
rm -r ${tempdir}
'''
dependencies = ["oss", "ui", "wglib", "release-build"]
[tasks.setupupstream]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
mkdir -p ${UPSTREAM_REPO_PATH}
if [ ! -d ${UPSTREAM_REPO_PATH}/.git ]; then
git clone ${UPSTREAM_REPO} ${UPSTREAM_REPO_PATH}
cd ${UPSTREAM_REPO_PATH} && git submodule update --init && cd -
fi
cd ${UPSTREAM_REPO_PATH}
git fetch --all --tags
git checkout ${UPSTREAM_REV}
'''
[tasks.wglib]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
cd ${UPSTREAM_REPO_PATH}
./wireguard/build-wireguard-go.sh "${TARGET}"
cp -r ${UPSTREAM_REPO_PATH}/build ${REPO_ROOT}
'''
dependencies = ["cargo-config", "setupupstream"]
[tasks.winfw]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
output_dir=${REPO_ROOT}/windows/${TARGET}
mkdir -p ${output_dir}
cd ${UPSTREAM_REPO_PATH}
IS_RELEASE=true CPP_BUILD_MODES=Release ./build-windows-modules.sh
cp -r ${UPSTREAM_REPO_PATH}/windows/winfw/bin/x64-Release/ ${output_dir}
'''
[tasks.windows]
workspace = false
dependencies = ["oss", "cargo-config", "setupupstream", "wglib", "ui", "winfw", "release-build"]
[tasks.windows-sign]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
set -eu
./nymvpn-packages/windows/sign.sh \
target/${TARGET}/release/nymvpn.exe \
target/${TARGET}/release/nymvpn-ui.exe \
target/${TARGET}/release/nymvpn-daemon.exe \
build/lib/${TARGET}/libwg.dll \
windows/${TARGET}/X64-Release/winfw.dll
'''
[tasks.only-msi]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
cd nymvpn-packages/windows
candle -ext WixUtilExtension -dAppVersion=${APP_VERSION} -out nymvpn.wixobj -arch x64 nymvpn.wsx
light -ext WixUtilExtension -dAppVersion=${APP_VERSION} -out nymvpn-x64-${APP_VERSION}.msi nymvpn.wixobj
'''
[tasks.sign-msi]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
set -eu
./nymvpn-packages/windows/sign.sh nymvpn-packages/windows/nymvpn-x64-${APP_VERSION}.msi
'''
[tasks.msi]
workspace = false
dependencies = ["windows", "windows-sign", "only-msi", "sign-msi"]
[tasks.cargo-config]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
root_dir=$(pwd)
mkdir -p ${root_dir}/.cargo
cp ${root_dir}/nymvpn-packages/cargo-config.toml ${root_dir}/.cargo/config
sd __REPO_ROOT__ "${root_dir}" ${root_dir}/.cargo/config
'''
[tasks.ui]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
cd nymvpn-ui
npm install
npm run build
'''
[tasks.volumes-for-build]
workspace = false
script = '''
docker volume rm nymvpn-app-builder || true
docker volume create nymvpn-app-builder-cargo-git
docker volume create nymvpn-app-builder-cargo-registry
docker volume create nymvpn-app-builder-cargo-target
docker volume create nymvpn-app-builder-go
docker volume create nymvpn-app-builder
'''
[tasks.clear-and-copy-source]
workspace = false
script = '''
docker run --rm -v ${PWD}:/source \
-v nymvpn-app-builder:/build \
ghcr.io/nymvpn/nymvpn-app-builder:${BUILDER_TAG} \
cp -r \
/source/nymvpn-assets \
/source/nymvpn-cli \
/source/nymvpn-config \
/source/nymvpn-controller \
/source/nymvpn-daemon \
/source/nymvpn-entity \
/source/nymvpn-migration \
/source/nymvpn-packages \
/source/nymvpn-server \
/source/nymvpn-types \
/source/nymvpn-ui \
/source/Cargo.toml \
/source/Cargo.lock \
/source/about.toml \
/source/Makefile.toml \
/source/.dockerignore \
/build/
docker run --rm \
-v nymvpn-app-builder:/build \
ghcr.io/nymvpn/nymvpn-app-builder:${BUILDER_TAG} chown -R root:root .
'''
[tasks.builder-shell]
workspace = false
command = "docker"
args = [
"run",
"--rm",
"-it",
"-v", "nymvpn-app-builder-cargo-git:/root/.cargo/git",
"-v", "nymvpn-app-builder-cargo-registry:/root/.cargo/registry",
"-v", "nymvpn-app-builder-cargo-target:/root/.cargo/target",
"-v", "nymvpn-app-builder-go:/root/go",
"-v", "nymvpn-app-builder:/build",
# To clone github repo using ssh
"-v", "${HOME}/.ssh:/root/.ssh",
"-e", "CARGO_NET_GIT_FETCH_WITH_CLI=true",
"-e", "TARGET=${TARGET}",
# tag created by builder task
"ghcr.io/nymvpn/nymvpn-app-builder:${BUILDER_TAG}",
"bash"
]
dependencies = ["volumes-for-build", "clear-and-copy-source"]
[tasks.linux-packages]
workspace = false
# only need to build once for packaging both
dependencies = ["deb", "rpm-no-build"]
[tasks.build-in-container]
workspace = false
command = "docker"
args = [
"run",
"--rm",
"-it",
"-v", "nymvpn-app-builder-cargo-git:/root/.cargo/git",
"-v", "nymvpn-app-builder-cargo-registry:/root/.cargo/registry",
"-v", "nymvpn-app-builder-cargo-target:/root/.cargo/target",
"-v", "nymvpn-app-builder-go:/root/go",
"-v", "nymvpn-app-builder:/build",
# To clone github repo using ssh
"-v", "${HOME}/.ssh:/root/.ssh",
"-e", "CARGO_NET_GIT_FETCH_WITH_CLI=true",
"-e", "TARGET=${TARGET}",
# tag created by builder task
"ghcr.io/nymvpn/nymvpn-app-builder:${BUILDER_TAG}",
"cargo",
"make",
"-e", "TARGET=${TARGET}",
"linux-packages",
]
dependencies = ["volumes-for-build", "clear-and-copy-source"]
[tasks.output-dir]
workspace = false
script = '''
echo "TARGET: ${TARGET}"
mkdir -p ${PWD}/dist/${TARGET}
'''
[tasks.linux]
workspace = false
command = "docker"
args = [
"run",
"--rm",
"-it",
"-v", "nymvpn-app-builder-cargo-target:/root/.cargo/target",
"-v", "${PWD}/dist:/dist",
# tag created by builder task
"ghcr.io/nymvpn/nymvpn-app-builder:${BUILDER_TAG}",
"cp", "-r",
"/root/.cargo/target/${TARGET}/debian", "/root/.cargo/target/${TARGET}/generate-rpm",
"/dist/${TARGET}",
]
dependencies = ["output-dir", "build-in-container"]
[tasks.icon]
workspace = false
script = '''
cd nymvpn-ui
cargo tauri icon -o ../nymvpn-assets/icons ../nymvpn-assets/app-icon.png
'''
[tasks.oss]
workspace = false
script_runner = "sh"
script = '''
#!/usr/bin/env bash
set -eu
cargo about generate nymvpn-packages/nymvpn-oss-licenses-rust.hbs > nymvpn-packages/nymvpn-oss-licenses-rust.html
sd "(talpid-[\w-]+|tunnel-obfuscation) (\d+.\d+.\d+)" '$1 $2 Copyright (C) Mullvad VPN AB, GPL-3.0' nymvpn-packages/nymvpn-oss-licenses-rust.html
sd "https://crates.io/crates//(talpid-\w+|tunnel-obfuscation)" 'https://github.com/mullvad/mullvadvpn-app/tree/main/$1' nymvpn-packages/nymvpn-oss-licenses-rust.html
cd nymvpn-ui && npm i && cd ..
npx --yes generate-license-file --overwrite --ci --input nymvpn-ui/package.json --output nymvpn-packages/third-party-licenses.txt
cat nymvpn-packages/nymvpn-oss-licenses-header.hbs > nymvpn-packages/nymvpn-oss-licenses.html
cat nymvpn-packages/nymvpn-oss-licenses-rust.html >> nymvpn-packages/nymvpn-oss-licenses.html
cat nymvpn-packages/third-party-licenses.txt >> nymvpn-packages/nymvpn-oss-licenses.html
cat nymvpn-packages/nymvpn-oss-licenses-footer.hbs >> nymvpn-packages/nymvpn-oss-licenses.html
'''
[tasks.android-init]
workspace = false
script = '''
git clone https://github.com:WireGuard/wireguard-tools.git nymvpn-android/app/tunnel/wireguard-tools
cd nymvpn-android/app/tunnel/wireguard-tools && git checkout b4f6b4f229d291daf7c35c6f1e7f4841cc6d69bc && cd -
git clone https://github.com:termux/termux-elf-cleaner.git nymvpn-android/app/tunnel/elf-cleaner
cd nymvpn-android/app/tunnel/elf-cleaner && git checkout 7efc05090675ec6161b7def862728086a26c3b1f && cd -
'''
+12
View File
@@ -0,0 +1,12 @@
# Network Dependency
nymvpn takes its design inspiration from [mullvadvpn-app](https://github.com/mullvad/mullvadvpn-app).
For example Actor pattern is foundational in daemon.
However, nymvpn uses different design/technologies than mullvadvpn-app in many places: for persisting data in sqlite (sea-orm), desktop app (Tauri), packaging for Windows (Wix), Toml for configuration files, GRPC for backend API.
nymvpn uses "talpid" crates for all of the client side networking from mullvadvpn-app project, with minimal modification. For example, interface on Linux would be named "nymvpn".
The modifications to upstream mullvadvpn-app can be found [here](https://github.com/upvpn/mullvadvpn-app)
We're not affiliated with Mullvad, and we're grateful for their high quality open source projects. If nymvpn doesn't offer what you're looking for please see VPN offering from Mullvad at https://mullvad.net/
+63
View File
@@ -0,0 +1,63 @@
<div align="center">
<a href="https://nymvpn.net">
<img src="./nymvpn-assets/icons/Square71x71Logo.png" >
</a>
<h3 align="center">nymvpn</h3>
<h4 align="center">A Modern Serverless VPN</h4>
<img src="nymvpn-assets/cli.gif" />
</div>
# nymvpn
nymvpn (pronounced Up VPN) app is WireGuard VPN client for Linux, macOS, Windows, and Android.
For more information please visit https://nymvpn.net
nymvpn desktop app is made up of UI, CLI and background Daemon.
# Serverless
nymvpn uses Serverless computing model, where a Linux based WireGuard server is provisioned on public cloud providers when app requests to connect to VPN. And server is deprovisioned when app requests to disconnect from VPN.
All of it happens with a single click or tap on the UI, or a single command on terminal.
# Install
App for Linux, macOS, Windows, and Android is available for download on [Github Releases](https://github.com/nymvpn/nymvpn-app/releases) or on website at https://nymvpn.net/download
# Code
## Organization
| Crate or Directory | Description |
| --- | --- |
| nymvpn-android | Standalone app for Android. |
| nymvpn-cli | Code for `nymvpn` cli. |
| nymvpn-config | Configuration read from env vars, `nymvpn.conf.toml` are merged at runtime in `nymvpn-config` and is source of runtime configuration for `nymvpn-cli`, `nymvpn-daemon`, and `nymvpn-ui`. |
| nymvpn-controller | Defines GRPC protobuf for APIs exposed by `nymvpn-daemon` to be consumed by `nymvpn-cli` and `nymvpn-ui`. |
| nymvpn-daemon | Daemon is responsible for orchestrating a VPN session. It takes input from nymvpn-cli or nymvpn-ui via GRPC (defined in `nymvpn-controller`) and make calls to backend server via separate GRPC (defined in `nymvpn-server`). When backend informs that a server is ready daemon configures network tunnel, see [NetworkDependency.md](./NetworkDependency.md) for more info. |
| nymvpn-entity | Defines data models used by nymvpn-daemon to persist data on disk in sqlite database. |
| nymvpn-migration | Defines database migration from which `nymvpn-entity` is generated. |
| nymvpn-packages| Contains resources to package binaries for distribution on macOS (pkg), Linux (rpm & deb), and Windows (msi). |
|nymvpn-server| Contains GRPC protobuf definitions and code for communication with backend server. |
| nymvpn-types | Defines common Rust types for data types used in various crates. These are also used to generate Typescript types for nymvpn-ui for seamless serialization and deserialization across language boundaries. |
|nymvpn-ui| A Tauri based desktop app. GPRC communication with daemon is done in Rust. Typescript code interact with Rust code via Tauri commands. |
## Building Desktop Apps
Please see [Build.md](./Build.md)
## Building Android App
Please see [nymvpn-android/README.md](./nymvpn-android/README.md)
# License
Android app, and all Rust crates in this repository are [licensed under GPL version 3](./LICENSE).
Copyright (C) 2023 Nym Technologies S.A.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
+3
View File
@@ -0,0 +1,3 @@
# Security
If you find security issues/vulnerabilities, please report them via security@nymvpn.net with details about the vulnerability/issue. Security issue should not be reported via public Github Issue tracker.
+30
View File
@@ -0,0 +1,30 @@
accepted = [
"Apache-2.0",
"MIT",
"GPL-3.0",
"MPL-2.0",
"ISC",
"BSD-3-Clause",
"BSD-2-Clause",
"Unicode-DFS-2016",
"WTFPL",
"OpenSSL",
"NOASSERTION",
"Apache-2.0 WITH LLVM-exception",
]
workarounds = [
"ring",
"rustls",
]
targets = [
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"aarch64-apple-darwin",
"x86_64-pc-windows-msvc",
]
ignore-build-dependencies = true
ignore-dev-dependencies = true
Binary file not shown.

After

Width:  |  Height:  |  Size: 545 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 KiB

Binary file not shown.
+659
View File
@@ -0,0 +1,659 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" viewBox="0 0 2480 2480" enable-background="new 0 0 2480 2480" xml:space="preserve">
<path fill="#000000" opacity="1.000000" stroke="none"
d="
M2.000000,1156.000000
C2.000000,771.474121 2.000000,386.948273 2.000000,2.000000
C386.718231,2.000000 771.436646,2.000000 1157.575928,2.774093
C1156.895752,4.252493 1154.836182,5.386602 1152.687378,5.593789
C1133.805298,7.414357 1114.907104,9.069780 1095.226807,10.759283
C1084.722656,12.009069 1074.967529,13.036946 1065.296387,14.588613
C1046.388062,17.622320 1027.440552,20.518517 1008.669189,24.271704
C988.973267,28.209772 969.464844,33.085560 949.059448,37.567116
C946.606445,37.857994 944.916931,37.960854 943.338196,38.433754
C905.230347,49.848984 866.958191,60.757950 829.109192,72.977303
C809.630737,79.265816 790.909973,87.901482 771.094727,95.550079
C708.056335,120.873268 648.601501,151.681488 591.455200,186.981873
C581.890991,192.889832 572.727356,199.446106 562.717407,205.841736
C485.335510,255.803986 415.925385,314.480255 351.878967,379.639160
C350.056000,381.493774 348.766235,383.872528 346.598083,386.351074
C336.670746,396.616455 326.916168,406.152496 318.166016,416.535034
C296.527496,442.210358 274.620392,467.712158 254.140381,494.299805
C214.260880,546.072327 179.360336,601.135254 148.695190,658.878784
C118.683922,715.390991 92.776169,773.679565 71.820473,834.158081
C50.995865,894.258301 34.644413,955.509277 23.362127,1018.139221
C16.235693,1057.699341 10.648326,1097.425903 7.645359,1137.512329
C7.224460,1143.130981 6.797827,1148.756470 6.088929,1154.342651
C5.794058,1156.666138 4.936373,1159.359985 2.000000,1156.000000
z"/>
<path fill="#000000" opacity="1.000000" stroke="none"
d="
M1144.000000,2482.000000
C763.472534,2482.000000 382.945099,2482.000000 2.000000,2482.000000
C2.000000,2092.614990 2.000000,1703.229980 2.840354,1312.796387
C4.341088,1313.846558 5.403334,1315.909912 5.592262,1318.050293
C6.408852,1327.301758 7.018379,1336.572510 7.615459,1345.841919
C10.071630,1383.973022 15.361381,1421.746460 21.943558,1459.353882
C31.523199,1514.087402 44.837513,1567.894409 61.760971,1620.828613
C86.347641,1697.732544 118.251434,1771.449707 157.290771,1842.110718
C196.026291,1912.221802 241.077148,1977.993042 292.739014,2039.196167
C304.506073,2053.136963 317.056641,2066.416016 329.501801,2080.542725
C338.064240,2090.107910 345.990631,2099.520020 354.754700,2108.074463
C375.889771,2128.703857 397.016846,2149.373779 418.914001,2169.179443
C435.849579,2184.497559 453.947357,2198.531006 471.971558,2213.595947
C480.738647,2220.616211 488.968445,2227.299561 497.402283,2233.714600
C538.868896,2265.256348 582.676025,2293.294678 627.428223,2319.866211
C637.853210,2326.055908 649.016357,2331.002197 660.245605,2337.048828
C661.758423,2338.312500 662.805664,2339.164307 663.980896,2339.765869
C705.744995,2361.139404 748.015625,2381.423828 791.896851,2398.136475
C807.088379,2403.922363 822.536743,2409.033447 838.378052,2414.957031
C882.580322,2430.489990 926.889465,2443.370117 972.065918,2453.111572
C987.937622,2456.534180 1003.978516,2459.171875 1020.532410,2462.681152
C1053.641235,2467.597900 1086.152588,2472.055908 1118.684082,2476.362061
C1125.906372,2477.318115 1133.209839,2477.647217 1140.452515,2478.470947
C1142.249756,2478.675537 1145.452026,2478.209473 1144.000000,2482.000000
z"/>
<path fill="#000000" opacity="1.000000" stroke="none"
d="
M1330.000000,2.000000
C1713.857544,2.000000 2097.715088,2.000000 2482.000000,2.000000
C2482.000000,377.384888 2482.000000,752.769958 2481.270508,1129.683350
C2479.812988,1129.458130 2478.641357,1127.757446 2478.425049,1125.942993
C2476.303955,1108.153687 2474.590576,1090.313354 2472.288330,1072.548706
C2465.317139,1018.756775 2454.290039,965.764282 2439.924805,913.480469
C2410.317139,805.719543 2366.872070,703.805603 2309.638672,607.834778
C2279.080322,556.593994 2245.477295,507.387878 2207.102783,461.646027
C2185.742920,436.185822 2163.918457,411.044861 2141.083740,386.914246
C2120.303955,364.955353 2097.875244,344.556519 2075.900391,322.875854
C2049.050293,300.195190 2023.122681,277.266205 1995.772095,256.183319
C1919.572388,197.446091 1837.257935,148.730927 1749.337158,109.649597
C1727.213379,99.815414 1704.468994,91.377357 1681.312744,82.023506
C1661.278564,74.856789 1642.080688,67.535522 1622.562744,61.200005
C1605.227905,55.573128 1587.538086,51.039959 1569.348145,45.649433
C1535.447754,37.638149 1502.300781,29.539127 1468.921631,22.549688
C1450.818604,18.759012 1432.319946,16.858213 1413.201904,13.909479
C1398.085693,11.913866 1383.790894,9.888351 1369.441406,8.383907
C1357.681641,7.150996 1345.856567,6.545444 1334.061523,5.645791
C1331.152710,5.423922 1331.154907,5.395327 1330.000000,2.000000
z"/>
<path fill="#000000" opacity="1.000000" stroke="none"
d="
M2482.000000,1354.000000
C2482.000000,1729.859741 2482.000000,2105.719482 2482.000000,2482.000000
C2103.281738,2482.000000 1724.563354,2482.000000 1344.315430,2481.265137
C1344.194214,2479.820801 1345.551147,2478.641357 1347.019287,2478.481201
C1354.873779,2477.624268 1362.792847,2477.302979 1370.622192,2476.279785
C1398.529907,2472.632324 1426.410522,2468.776123 1455.144531,2464.862793
C1484.524414,2458.810791 1513.163086,2453.338135 1541.560669,2446.816162
C1559.249390,2442.753906 1576.573853,2437.106201 1594.844482,2432.142090
C1600.700806,2430.654297 1605.865479,2429.451660 1610.832886,2427.692139
C1641.329224,2416.889404 1671.780884,2405.960693 1702.995361,2394.897949
C1714.292969,2390.337891 1725.003662,2386.301025 1735.360352,2381.500488
C1760.088379,2370.039062 1784.854858,2358.629395 1809.236816,2346.459473
C1869.238037,2316.510742 1925.746704,2280.830078 1979.677734,2241.039062
C2015.833496,2214.363281 2050.923828,2186.328613 2083.269287,2155.113281
C2110.550537,2128.784912 2137.848877,2102.374756 2163.551514,2074.534912
C2197.400391,2037.871460 2228.250977,1998.649048 2257.070068,1957.831177
C2302.509521,1893.473022 2341.425049,1825.434570 2374.022217,1753.742676
C2402.992188,1690.027466 2426.233154,1624.269775 2443.892578,1556.565674
C2459.084717,1498.321045 2470.223145,1439.301270 2476.330078,1379.378052
C2477.070068,1372.116943 2477.632080,1364.836426 2478.475586,1357.588135
C2478.686279,1355.777344 2478.205322,1352.590210 2482.000000,1354.000000
z"/>
<path fill="#FD0059" opacity="1.000000" stroke="none"
d="
M2482.000000,1353.062744
C2478.205322,1352.590210 2478.686279,1355.777344 2478.475586,1357.588135
C2477.632080,1364.836426 2477.070068,1372.116943 2476.330078,1379.378052
C2470.223145,1439.301270 2459.084717,1498.321045 2443.892578,1556.565674
C2426.233154,1624.269775 2402.992188,1690.027466 2374.022217,1753.742676
C2341.425049,1825.434570 2302.509521,1893.473022 2257.070068,1957.831177
C2228.250977,1998.649048 2197.400391,2037.871460 2163.551514,2074.534912
C2137.848877,2102.374756 2110.550537,2128.784912 2083.269287,2155.113281
C2050.923828,2186.328613 2015.833496,2214.363281 1979.677734,2241.039062
C1925.746704,2280.830078 1869.238037,2316.510742 1809.236816,2346.459473
C1784.854858,2358.629395 1760.088379,2370.039062 1735.360352,2381.500488
C1725.003662,2386.301025 1714.292969,2390.337891 1702.934937,2393.890381
C1702.222534,2384.750732 1700.063599,2375.522217 1702.984741,2368.367676
C1706.637573,2359.420410 1703.389404,2351.443115 1703.707520,2343.142334
C1704.223755,2329.672607 1705.858032,2316.132568 1704.636475,2302.210449
C1706.131104,2301.343262 1706.999023,2300.838623 1707.911499,2300.435059
C1872.842163,2227.512939 2013.766113,2122.305664 2129.126953,1983.796509
C2295.175537,1784.427856 2384.085449,1554.437256 2397.438477,1295.429810
C2400.011230,1245.527954 2398.809570,1195.543945 2394.555176,1145.641113
C2389.225586,1083.130127 2379.285156,1021.439026 2363.923584,960.643433
C2319.840820,786.178650 2239.296875,630.459351 2122.668213,493.482666
C2104.113770,471.691101 2084.243408,451.019867 2065.204102,428.914398
C2066.549561,423.919678 2068.180664,419.901825 2068.675049,415.748596
C2069.418701,409.502869 2074.961914,403.035431 2069.638428,396.963745
C2062.773438,389.134186 2062.122070,381.922882 2069.412598,374.247559
C2069.738037,373.904999 2069.454346,372.983826 2069.454346,371.716705
C2065.993652,368.276642 2060.073975,365.611786 2062.831055,358.350494
C2064.850586,353.031799 2069.009521,354.874359 2072.047607,355.040344
C2072.672607,349.993500 2073.195801,345.550232 2073.778809,341.114838
C2074.552490,335.230011 2075.373047,329.351349 2076.173340,323.470032
C2097.875244,344.556519 2120.303955,364.955353 2141.083740,386.914246
C2163.918457,411.044861 2185.742920,436.185822 2207.102783,461.646027
C2245.477295,507.387878 2279.080322,556.593994 2309.638672,607.834778
C2366.872070,703.805603 2410.317139,805.719543 2439.924805,913.480469
C2454.290039,965.764282 2465.317139,1018.756775 2472.288330,1072.548706
C2474.590576,1090.313354 2476.303955,1108.153687 2478.425049,1125.942993
C2478.641357,1127.757446 2479.812988,1129.458130 2481.270508,1130.605713
C2482.000000,1204.041748 2482.000000,1278.083618 2482.000000,1353.062744
z"/>
<path fill="#FD6141" opacity="1.000000" stroke="none"
d="
M329.246643,2079.999756
C317.056641,2066.416016 304.506073,2053.136963 292.739014,2039.196167
C241.077148,1977.993042 196.026291,1912.221802 157.290771,1842.110718
C118.251434,1771.449707 86.347641,1697.732544 61.760971,1620.828613
C44.837513,1567.894409 31.523199,1514.087402 21.943558,1459.353882
C15.361381,1421.746460 10.071630,1383.973022 7.615459,1345.841919
C7.018379,1336.572510 6.408852,1327.301758 5.592262,1318.050293
C5.403334,1315.909912 4.341088,1313.846558 2.840354,1311.873901
C2.000000,1272.624878 2.000000,1233.249756 2.696669,1192.739746
C3.783891,1185.246948 4.191141,1178.889771 4.456792,1172.526733
C4.462687,1172.385498 2.854996,1172.177124 2.000000,1172.000000
C2.000000,1167.258911 2.000000,1162.517700 2.000000,1156.888306
C4.936373,1159.359985 5.794058,1156.666138 6.088929,1154.342651
C6.797827,1148.756470 7.224460,1143.130981 7.645359,1137.512329
C10.648326,1097.425903 16.235693,1057.699341 23.362127,1018.139221
C34.644413,955.509277 50.995865,894.258301 71.820473,834.158081
C92.776169,773.679565 118.683922,715.390991 148.695190,658.878784
C179.360336,601.135254 214.260880,546.072327 254.140381,494.299805
C274.620392,467.712158 296.527496,442.210358 318.166016,416.535034
C326.916168,406.152496 336.670746,396.616455 346.819336,387.298340
C349.111511,397.039673 350.702759,406.157318 351.959839,415.320801
C354.039307,430.478943 347.020142,445.613251 351.698639,460.841003
C352.030396,461.920807 351.116547,463.384277 350.778839,464.668579
C347.058563,478.817993 346.556641,492.851044 352.077698,507.275665
C288.410614,585.358521 235.344467,669.326538 193.521423,760.365295
C126.249481,906.800049 91.659683,1060.733765 88.462120,1221.678833
C86.775948,1306.550293 94.636253,1390.659058 111.634445,1473.912476
C141.932693,1622.306519 199.339981,1759.333130 283.605133,1885.092407
C298.609344,1907.485229 314.815765,1929.072510 330.144287,1951.775146
C329.368286,1953.753174 328.206390,1955.273804 328.571472,1956.177979
C332.204346,1965.176758 325.094452,1975.258179 331.436707,1983.996216
C332.000854,1984.773682 331.152161,1986.519043 331.084595,1987.821411
C330.446564,2000.127197 326.730957,2012.421387 331.588593,2024.754395
C332.597107,2027.314941 331.849121,2030.640747 331.636078,2033.593994
C331.187073,2039.818726 330.346924,2046.020508 330.049316,2052.250000
C329.607788,2061.491455 329.497742,2070.749023 329.246643,2079.999756
z"/>
<path fill="#FD2B4F" opacity="1.000000" stroke="none"
d="
M1329.062744,2.000000
C1331.154907,5.395327 1331.152710,5.423922 1334.061523,5.645791
C1345.856567,6.545444 1357.681641,7.150996 1369.441406,8.383907
C1383.790894,9.888351 1398.085693,11.913866 1413.202148,14.792110
C1414.666626,32.711697 1415.932007,49.558369 1415.786743,66.392868
C1415.692139,77.369125 1415.896729,88.638809 1410.759399,99.196487
C1365.071655,93.011742 1320.113159,88.816185 1274.882202,87.850662
C1265.919922,87.659348 1256.959106,87.391411 1247.998047,86.333542
C1248.799072,75.686798 1249.405029,65.844688 1250.443115,56.048374
C1252.278687,38.724487 1255.235596,21.364594 1246.212402,4.940386
C1245.782349,4.157671 1246.054688,2.988963 1246.000000,2.000000
C1273.375122,2.000000 1300.750244,2.000000 1329.062744,2.000000
z"/>
<path fill="#FE354D" opacity="1.000000" stroke="none"
d="
M1245.062744,2.000000
C1246.054688,2.988963 1245.782349,4.157671 1246.212402,4.940386
C1255.235596,21.364594 1252.278687,38.724487 1250.443115,56.048374
C1249.405029,65.844688 1248.799072,75.686798 1247.077271,86.517754
C1211.969727,88.932030 1177.771362,90.099472 1143.602539,91.846756
C1127.705322,92.659691 1111.865723,94.598114 1095.994629,95.169136
C1095.326050,86.458298 1094.384521,78.615601 1094.075439,70.748077
C1093.741943,62.257504 1093.748291,53.734406 1094.061401,45.242275
C1094.485352,33.744526 1095.342529,22.262749 1096.013794,10.774117
C1114.907104,9.069780 1133.805298,7.414357 1152.687378,5.593789
C1154.836182,5.386602 1156.895752,4.252493 1158.498291,2.774093
C1186.708496,2.000000 1215.416870,2.000000 1245.062744,2.000000
z"/>
<path fill="#FD394C" opacity="1.000000" stroke="none"
d="
M1144.929321,2482.000000
C1145.452026,2478.209473 1142.249756,2478.675537 1140.452515,2478.470947
C1133.209839,2477.647217 1125.906372,2477.318115 1118.684082,2476.362061
C1086.152588,2472.055908 1053.641235,2467.597900 1020.548401,2461.732910
C1020.201294,2432.329346 1019.038818,2404.386963 1020.798401,2376.385742
C1046.574341,2380.301758 1071.468262,2384.903809 1096.542603,2388.083008
C1123.607178,2391.513916 1150.840332,2393.614746 1177.999512,2397.110840
C1177.611816,2400.520264 1177.691772,2403.297852 1176.759033,2405.678467
C1171.955933,2417.938232 1173.241089,2430.170654 1176.160400,2442.541748
C1176.962402,2445.940430 1178.116089,2450.099609 1176.900391,2452.976562
C1172.930420,2462.370850 1172.538696,2472.150391 1172.000000,2482.000000
C1163.286255,2482.000000 1154.572388,2482.000000 1144.929321,2482.000000
z"/>
<path fill="#FE304F" opacity="1.000000" stroke="none"
d="
M1172.937256,2482.000000
C1172.538696,2472.150391 1172.930420,2462.370850 1176.900391,2452.976562
C1178.116089,2450.099609 1176.962402,2445.940430 1176.160400,2442.541748
C1173.241089,2430.170654 1171.955933,2417.938232 1176.759033,2405.678467
C1177.691772,2403.297852 1177.611816,2400.520264 1178.914795,2396.979980
C1203.020508,2396.593018 1226.212036,2397.644043 1249.400146,2397.574951
C1273.576660,2397.503174 1297.750000,2396.351562 1321.944946,2396.591064
C1321.475098,2404.433838 1319.269287,2411.799805 1320.912476,2418.173584
C1323.160034,2426.891846 1321.982422,2435.237061 1321.923340,2443.743896
C1321.896240,2447.656738 1320.806519,2451.571289 1320.874023,2455.473389
C1321.026978,2464.319580 1321.598633,2473.158203 1322.000000,2482.000000
C1272.624878,2482.000000 1223.249756,2482.000000 1172.937256,2482.000000
z"/>
<path fill="#FD2750" opacity="1.000000" stroke="none"
d="
M1322.916138,2482.000000
C1321.598633,2473.158203 1321.026978,2464.319580 1320.874023,2455.473389
C1320.806519,2451.571289 1321.896240,2447.656738 1321.923340,2443.743896
C1321.982422,2435.237061 1323.160034,2426.891846 1320.912476,2418.173584
C1319.269287,2411.799805 1321.475098,2404.433838 1322.672119,2396.228027
C1367.118774,2392.044434 1410.483154,2386.102783 1453.778076,2379.750977
C1454.581421,2384.850098 1455.577515,2389.028809 1455.946533,2393.262207
C1456.789307,2402.932617 1457.568604,2412.621094 1457.905762,2422.318604
C1458.157104,2429.546875 1459.443726,2437.461426 1457.050049,2443.866943
C1454.363892,2451.054932 1453.953247,2457.795410 1454.301392,2464.999023
C1426.410522,2468.776123 1398.529907,2472.632324 1370.622192,2476.279785
C1362.792847,2477.302979 1354.873779,2477.624268 1347.019287,2478.481201
C1345.551147,2478.641357 1344.194214,2479.820801 1343.393066,2481.265137
C1337.277466,2482.000000 1330.554810,2482.000000 1322.916138,2482.000000
z"/>
<path fill="#111726" opacity="1.000000" stroke="none"
d="
M2.000000,1172.908203
C2.854996,1172.177124 4.462687,1172.385498 4.456792,1172.526733
C4.191141,1178.889771 3.783891,1185.246948 2.696669,1191.802368
C2.000000,1185.938843 2.000000,1179.877563 2.000000,1172.908203
z"/>
<path fill="#FD5843" opacity="1.000000" stroke="none"
d="
M352.295471,506.658752
C346.556641,492.851044 347.058563,478.817993 350.778839,464.668579
C351.116547,463.384277 352.030396,461.920807 351.698639,460.841003
C347.020142,445.613251 354.039307,430.478943 351.959839,415.320801
C350.702759,406.157318 349.111511,397.039673 347.449158,386.954163
C348.766235,383.872528 350.056000,381.493774 351.878967,379.639160
C415.925385,314.480255 485.335510,255.803986 563.398560,206.183746
C569.218628,209.227905 571.037903,212.937622 568.957886,218.201599
C566.186829,225.214447 566.327942,232.258118 568.689331,239.435516
C569.473450,241.818924 569.378967,244.587112 569.243042,247.155945
C569.017273,251.420425 568.140991,255.659927 568.053833,259.920502
C567.772949,273.652466 570.280884,287.473389 566.584961,301.114685
C566.366821,301.919708 567.879028,303.193573 568.194397,304.815002
C505.921600,350.333984 448.781769,400.660156 396.817505,456.793304
C381.682434,473.142609 367.120239,490.022217 352.295471,506.658752
z"/>
<path fill="#FD4E47" opacity="1.000000" stroke="none"
d="
M568.585999,304.249237
C567.879028,303.193573 566.366821,301.919708 566.584961,301.114685
C570.280884,287.473389 567.772949,273.652466 568.053833,259.920502
C568.140991,255.659927 569.017273,251.420425 569.243042,247.155945
C569.378967,244.587112 569.473450,241.818924 568.689331,239.435516
C566.327942,232.258118 566.186829,225.214447 568.957886,218.201599
C571.037903,212.937622 569.218628,209.227905 564.059692,206.044830
C572.727356,199.446106 581.890991,192.889832 591.455200,186.981873
C648.601501,151.681488 708.056335,120.873268 771.135132,96.496933
C772.619385,105.286263 773.718445,113.192955 773.899719,121.120636
C774.125427,130.992645 774.821960,141.104767 773.079834,150.707703
C770.877075,162.849945 772.948120,173.603149 777.916504,184.683075
C718.252808,211.524277 661.518799,242.315765 607.511963,278.085876
C594.478088,286.718567 581.558289,295.523499 568.585999,304.249237
z"/>
<path fill="#FD4449" opacity="1.000000" stroke="none"
d="
M778.408386,184.169250
C772.948120,173.603149 770.877075,162.849945 773.079834,150.707703
C774.821960,141.104767 774.125427,130.992645 773.899719,121.120636
C773.718445,113.192955 772.619385,105.286263 771.887207,96.423393
C790.909973,87.901482 809.630737,79.265816 829.109192,72.977303
C866.958191,60.757950 905.230347,49.848984 943.338196,38.433754
C944.916931,37.960854 946.606445,37.857994 949.092712,38.511047
C950.955872,50.275467 951.446899,61.196079 953.177063,71.916702
C954.258545,78.617790 954.122559,84.451302 949.379639,89.509445
C947.113892,91.925690 946.649414,94.198669 947.378784,97.554909
C949.268311,106.248917 950.501343,115.085594 951.341187,124.241501
C909.276917,135.853394 868.375549,148.669510 828.369080,164.223862
C811.656860,170.721512 795.058533,177.511948 778.408386,184.169250
z"/>
<path fill="#FD3D4B" opacity="1.000000" stroke="none"
d="
M951.996399,123.865334
C950.501343,115.085594 949.268311,106.248917 947.378784,97.554909
C946.649414,94.198669 947.113892,91.925690 949.379639,89.509445
C954.122559,84.451302 954.258545,78.617790 953.177063,71.916702
C951.446899,61.196079 950.955872,50.275467 949.907776,38.495461
C969.464844,33.085560 988.973267,28.209772 1008.669189,24.271704
C1027.440552,20.518517 1046.388062,17.622320 1065.296387,14.588613
C1074.967529,13.036946 1084.722656,12.009069 1095.226807,10.759282
C1095.342529,22.262749 1094.485352,33.744526 1094.061401,45.242275
C1093.748291,53.734406 1093.741943,62.257504 1094.075439,70.748077
C1094.384521,78.615601 1095.326050,86.458298 1095.269531,95.504791
C1064.811768,101.782570 1035.014771,106.551987 1005.357117,112.070274
C987.458313,115.400620 969.776001,119.894356 951.996399,123.865334
z"/>
<path fill="#FE5345" opacity="1.000000" stroke="none"
d="
M659.839844,2336.520508
C649.016357,2331.002197 637.853210,2326.055908 627.428223,2319.866211
C582.676025,2293.294678 538.868896,2265.256348 497.402283,2233.714600
C488.968445,2227.299561 480.738647,2220.616211 472.323059,2212.748535
C472.497803,2210.835693 472.882416,2210.254639 473.013123,2209.621338
C475.315247,2198.465088 471.920898,2186.470947 477.984131,2175.864990
C476.047424,2154.467529 482.650940,2132.597900 474.373993,2111.514648
C473.574524,2109.478516 474.074158,2106.932129 474.551453,2104.885742
C512.278076,2137.957031 550.918823,2168.899414 592.145081,2196.422852
C614.526550,2211.364990 635.941956,2227.859131 659.997437,2241.080811
C660.670776,2249.479492 661.500549,2257.026611 661.963074,2264.596191
C662.749268,2277.463135 663.659729,2290.340088 663.812256,2303.220947
C663.893433,2310.079834 662.561646,2316.964355 661.739685,2323.821777
C661.230530,2328.069580 660.481384,2332.288574 659.839844,2336.520508
z"/>
<path fill="#FE5B43" opacity="1.000000" stroke="none"
d="
M473.970764,2104.622559
C474.074158,2106.932129 473.574524,2109.478516 474.373993,2111.514648
C482.650940,2132.597900 476.047424,2154.467529 477.984131,2175.864990
C471.920898,2186.470947 475.315247,2198.465088 473.013123,2209.621338
C472.882416,2210.254639 472.497803,2210.835693 471.879852,2212.288086
C453.947357,2198.531006 435.849579,2184.497559 418.914001,2169.179443
C397.016846,2149.373779 375.889771,2128.703857 354.754700,2108.074463
C345.990631,2099.520020 338.064240,2090.107910 329.501770,2080.542725
C329.497742,2070.749023 329.607788,2061.491455 330.049316,2052.250000
C330.346924,2046.020508 331.187073,2039.818726 331.636078,2033.593994
C331.849121,2030.640747 332.597107,2027.314941 331.588593,2024.754395
C326.730957,2012.421387 330.446564,2000.127197 331.084595,1987.821411
C331.152161,1986.519043 332.000854,1984.773682 331.436707,1983.996216
C325.094452,1975.258179 332.204346,1965.176758 328.571472,1956.177979
C328.206390,1955.273804 329.368286,1953.753174 330.713379,1952.173584
C360.199677,1988.093140 390.371094,2022.983643 423.096802,2055.587646
C439.779816,2072.208496 456.995758,2088.294678 473.970764,2104.622559
z"/>
<path fill="#FD4A48" opacity="1.000000" stroke="none"
d="
M660.245667,2337.048828
C660.481384,2332.288574 661.230530,2328.069580 661.739685,2323.821777
C662.561646,2316.964355 663.893433,2310.079834 663.812256,2303.220947
C663.659729,2290.340088 662.749268,2277.463135 661.963074,2264.596191
C661.500549,2257.026611 660.670776,2249.479492 660.676819,2241.211914
C707.186646,2267.078613 754.482605,2290.716064 803.502991,2310.861328
C817.633301,2316.668701 831.822876,2322.331299 845.989258,2328.935303
C845.614258,2345.645020 845.523987,2361.480225 840.857300,2376.891602
C839.839905,2380.251709 839.015564,2385.068359 840.636963,2387.574463
C846.152283,2396.099609 843.788452,2403.249268 838.863953,2410.620605
C838.171448,2411.657227 838.182434,2413.163818 837.867798,2414.452881
C822.536743,2409.033447 807.088379,2403.922363 791.896851,2398.136475
C748.015625,2381.423828 705.744995,2361.139404 663.980896,2339.765869
C662.805664,2339.164307 661.758423,2338.312500 660.245667,2337.048828
z"/>
<path fill="#FD414A" opacity="1.000000" stroke="none"
d="
M838.378052,2414.957031
C838.182434,2413.163818 838.171448,2411.657227 838.863953,2410.620605
C843.788452,2403.249268 846.152283,2396.099609 840.636963,2387.574463
C839.015564,2385.068359 839.839905,2380.251709 840.857300,2376.891602
C845.523987,2361.480225 845.614258,2345.645020 846.744873,2328.954102
C880.444214,2338.418213 913.206848,2349.388428 946.401489,2358.842041
C970.638367,2365.744385 995.443848,2370.649658 1019.995911,2376.445068
C1019.038818,2404.386963 1020.201294,2432.329346 1019.958191,2461.219727
C1003.978516,2459.171875 987.937622,2456.534180 972.065918,2453.111572
C926.889465,2443.370117 882.580322,2430.489990 838.378052,2414.957031
z"/>
<path fill="#FD0155" opacity="1.000000" stroke="none"
d="
M2075.900391,322.875854
C2075.373047,329.351349 2074.552490,335.230011 2073.778809,341.114838
C2073.195801,345.550232 2072.672607,349.993500 2072.047607,355.040344
C2069.009521,354.874359 2064.850586,353.031799 2062.831055,358.350494
C2060.073975,365.611786 2065.993652,368.276642 2069.454346,371.716705
C2069.454346,372.983826 2069.738037,373.904999 2069.412598,374.247559
C2062.122070,381.922882 2062.773438,389.134186 2069.638428,396.963745
C2074.961914,403.035431 2069.418701,409.502869 2068.675049,415.748596
C2068.180664,419.901825 2066.549561,423.919678 2064.628418,428.515320
C2060.484619,425.789642 2057.117188,422.569153 2053.797119,419.300537
C1977.195312,343.888367 1891.729736,280.400085 1797.524536,228.641983
C1759.367310,207.677673 1719.990845,189.289185 1679.997559,171.621979
C1681.336304,152.104416 1682.968262,133.457199 1683.564331,114.776932
C1683.908936,103.978073 1682.583618,93.125916 1682.009888,82.297729
C1704.468994,91.377357 1727.213379,99.815414 1749.337158,109.649597
C1837.257935,148.730927 1919.572388,197.446091 1995.772095,256.183319
C2023.122681,277.266205 2049.050293,300.195190 2075.900391,322.875854
z"/>
<path fill="#FD1E51" opacity="1.000000" stroke="none"
d="
M1411.654297,99.205811
C1415.896729,88.638809 1415.692139,77.369125 1415.786743,66.392868
C1415.932007,49.558369 1414.666626,32.711697 1413.999878,14.987562
C1432.319946,16.858213 1450.818604,18.759012 1468.921631,22.549688
C1502.300781,29.539127 1535.447754,37.638149 1569.348145,46.577469
C1570.998779,62.309551 1567.693237,76.806503 1571.695679,91.289131
C1573.305176,97.112778 1572.783813,103.855881 1569.305542,109.689880
C1568.507080,111.029327 1568.292358,112.849648 1568.221680,114.466690
C1567.949707,120.681519 1567.845459,126.903702 1566.871338,132.991791
C1555.177734,129.928696 1544.303467,126.951164 1533.402710,124.073380
C1493.290771,113.483772 1452.558472,105.921730 1411.654297,99.205811
z"/>
<path fill="#FD0D53" opacity="1.000000" stroke="none"
d="
M1567.678589,133.123138
C1567.845459,126.903702 1567.949707,120.681519 1568.221680,114.466690
C1568.292358,112.849648 1568.507080,111.029327 1569.305542,109.689880
C1572.783813,103.855881 1573.305176,97.112778 1571.695679,91.289131
C1567.693237,76.806503 1570.998779,62.309551 1570.004883,46.952065
C1587.538086,51.039959 1605.227905,55.573128 1622.562744,61.200005
C1642.080688,67.535522 1661.278564,74.856789 1681.312744,82.023514
C1682.583618,93.125916 1683.908936,103.978073 1683.564331,114.776932
C1682.968262,133.457199 1681.336304,152.104416 1679.263672,171.466797
C1673.169434,170.057678 1668.030029,167.717163 1662.718018,165.874619
C1631.060303,154.893616 1599.362793,144.027695 1567.678589,133.123138
z"/>
<path fill="#FD1C52" opacity="1.000000" stroke="none"
d="
M1455.144531,2464.862793
C1453.953247,2457.795410 1454.363892,2451.054932 1457.050049,2443.866943
C1459.443726,2437.461426 1458.157104,2429.546875 1457.905762,2422.318604
C1457.568604,2412.621094 1456.789307,2402.932617 1455.946533,2393.262207
C1455.577515,2389.028809 1454.581421,2384.850098 1454.471191,2379.482422
C1483.844116,2371.806641 1512.689697,2365.602051 1541.359741,2358.670654
C1559.048218,2354.394287 1576.468872,2349.010742 1594.001831,2344.965820
C1593.330078,2350.676758 1591.985107,2355.567627 1592.139038,2360.410889
C1592.497925,2371.700684 1597.676025,2382.670166 1594.467163,2394.243652
C1594.392456,2394.513428 1594.699585,2394.866699 1594.714233,2395.186523
C1594.955322,2400.392334 1595.461060,2405.606201 1595.335571,2410.804199
C1595.163330,2417.932129 1594.509277,2425.048340 1594.061768,2432.169678
C1576.573853,2437.106201 1559.249390,2442.753906 1541.560669,2446.816162
C1513.163086,2453.338135 1484.524414,2458.810791 1455.144531,2464.862793
z"/>
<path fill="#FD0D53" opacity="1.000000" stroke="none"
d="
M1594.844482,2432.142090
C1594.509277,2425.048340 1595.163330,2417.932129 1595.335571,2410.804199
C1595.461060,2405.606201 1594.955322,2400.392334 1594.714233,2395.186523
C1594.699585,2394.866699 1594.392456,2394.513428 1594.467163,2394.243652
C1597.676025,2382.670166 1592.497925,2371.700684 1592.139038,2360.410889
C1591.985107,2355.567627 1593.330078,2350.676758 1594.686768,2344.658936
C1631.596558,2329.880371 1667.813477,2316.250244 1704.030518,2302.620117
C1705.858032,2316.132568 1704.223755,2329.672607 1703.707520,2343.142334
C1703.389404,2351.443115 1706.637573,2359.420410 1702.984741,2368.367676
C1700.063599,2375.522217 1702.222534,2384.750732 1702.187012,2394.066406
C1671.780884,2405.960693 1641.329224,2416.889404 1610.832886,2427.692139
C1605.865479,2429.451660 1600.700806,2430.654297 1594.844482,2432.142090
z"/>
<path fill="#111726" opacity="1.000000" stroke="none"
d="
M1704.636475,2302.210449
C1667.813477,2316.250244 1631.596558,2329.880371 1594.694580,2343.817383
C1576.468872,2349.010742 1559.048218,2354.394287 1541.359741,2358.670654
C1512.689697,2365.602051 1483.844116,2371.806641 1454.379395,2378.586670
C1410.483154,2386.102783 1367.118774,2392.044434 1322.651855,2395.309082
C1297.750000,2396.351562 1273.576660,2397.503174 1249.400146,2397.574951
C1226.212036,2397.644043 1203.020508,2396.593018 1178.915283,2396.165039
C1150.840332,2393.614746 1123.607178,2391.513916 1096.542603,2388.083008
C1071.468262,2384.903809 1046.574341,2380.301758 1020.798401,2376.385742
C995.443848,2370.649658 970.638367,2365.744385 946.401489,2358.842041
C913.206848,2349.388428 880.444214,2338.418213 846.740845,2328.078857
C831.822876,2322.331299 817.633301,2316.668701 803.502991,2310.861328
C754.482605,2290.716064 707.186646,2267.078613 660.668091,2240.370605
C635.941956,2227.859131 614.526550,2211.364990 592.145081,2196.422852
C550.918823,2168.899414 512.278076,2137.957031 474.551453,2104.885742
C456.995758,2088.294678 439.779816,2072.208496 423.096802,2055.587646
C390.371094,2022.983643 360.199677,1988.093140 331.036163,1951.430176
C314.815765,1929.072510 298.609344,1907.485229 283.605133,1885.092407
C199.339981,1759.333130 141.932693,1622.306519 111.634445,1473.912476
C94.636253,1390.659058 86.775948,1306.550293 88.462120,1221.678833
C91.659683,1060.733765 126.249481,906.800049 193.521423,760.365295
C235.344467,669.326538 288.410614,585.358521 352.077698,507.275696
C367.120239,490.022217 381.682434,473.142609 396.817505,456.793304
C448.781769,400.660156 505.921600,350.333984 568.194397,304.815002
C581.558289,295.523499 594.478088,286.718567 607.511963,278.085876
C661.518799,242.315765 718.252808,211.524277 777.916504,184.683075
C795.058533,177.511948 811.656860,170.721512 828.369080,164.223862
C868.375549,148.669510 909.276917,135.853394 951.341187,124.241501
C969.776001,119.894356 987.458313,115.400620 1005.357117,112.070274
C1035.014771,106.551987 1064.811768,101.782570 1095.275024,96.362015
C1111.865723,94.598114 1127.705322,92.659691 1143.602539,91.846756
C1177.771362,90.099472 1211.969727,88.932030 1247.076904,87.343018
C1256.959106,87.391411 1265.919922,87.659348 1274.882202,87.850662
C1320.113159,88.816185 1365.071655,93.011742 1410.759399,99.196487
C1452.558472,105.921730 1493.290771,113.483772 1533.402710,124.073380
C1544.303467,126.951164 1555.177734,129.928696 1566.871338,132.991791
C1599.362793,144.027695 1631.060303,154.893616 1662.718018,165.874619
C1668.030029,167.717163 1673.169434,170.057678 1679.122314,172.323257
C1719.990845,189.289185 1759.367310,207.677673 1797.524536,228.641983
C1891.729736,280.400085 1977.195312,343.888367 2053.797119,419.300537
C2057.117188,422.569153 2060.484619,425.789642 2064.405762,429.431580
C2084.243408,451.019867 2104.113770,471.691101 2122.668213,493.482666
C2239.296875,630.459351 2319.840820,786.178650 2363.923584,960.643433
C2379.285156,1021.439026 2389.225586,1083.130127 2394.555176,1145.641113
C2398.809570,1195.543945 2400.011230,1245.527954 2397.438477,1295.429810
C2384.085449,1554.437256 2295.175537,1784.427856 2129.126953,1983.796509
C2013.766113,2122.305664 1872.842163,2227.512939 1707.911499,2300.435059
C1706.999023,2300.838623 1706.131104,2301.343262 1704.636475,2302.210449
M1543.000000,1475.916992
C1560.885010,1475.916992 1578.770142,1475.916992 1597.815063,1475.916992
C1597.815063,1343.276855 1597.815063,1211.767822 1597.815063,1080.258789
C1598.370850,1080.218994 1598.926636,1080.179199 1599.482544,1080.139404
C1633.850098,1211.957520 1668.217651,1343.775513 1702.546143,1475.443481
C1769.731934,1475.443481 1835.939819,1475.443481 1902.799194,1475.443481
C1911.464722,1442.042969 1920.041992,1408.918823 1928.654541,1375.803833
C1937.197021,1342.959106 1945.775513,1310.123657 1954.337402,1277.283936
C1962.983276,1244.122070 1971.464111,1210.916138 1980.320435,1177.810669
C1989.040771,1145.213989 1996.857056,1112.357666 2006.236450,1079.943237
C2006.922363,1080.078369 2007.608154,1080.213501 2008.293945,1080.348511
C2008.293945,1211.892090 2008.293945,1343.435669 2008.293945,1475.259277
C2045.304443,1475.259277 2081.697754,1475.259277 2118.277832,1475.259277
C2118.277832,1319.766602 2118.277832,1164.932129 2118.277832,1009.858276
C2048.318604,1009.858276 1978.869995,1009.858276 1909.208252,1009.858276
C1874.011719,1144.833252 1838.957642,1279.261963 1803.903564,1413.690552
C1803.296875,1413.673828 1802.690063,1413.657227 1802.083374,1413.640625
C1766.868286,1278.899902 1731.653076,1144.159302 1696.539429,1009.806763
C1625.816528,1009.806763 1556.285645,1009.806763 1486.763916,1009.806763
C1486.763916,1165.253540 1486.763916,1320.110352 1486.763916,1475.916992
C1505.179443,1475.916992 1523.089722,1475.916992 1543.000000,1475.916992
M652.718262,1192.136841
C627.059082,1131.158569 601.399841,1070.180298 575.927673,1009.646484
C540.850159,1007.638428 376.362366,1008.593018 368.720612,1010.721680
C368.720612,1165.601807 368.720612,1320.420776 368.720612,1475.558838
C405.586884,1475.558838 441.804169,1475.558838 479.239441,1475.558838
C479.239441,1471.639893 479.239471,1468.072998 479.239471,1464.505981
C479.239471,1428.844482 479.239441,1393.183105 479.239471,1357.521606
C479.239532,1266.534912 479.228546,1175.548096 479.284668,1084.561401
C479.286682,1081.310669 478.380066,1077.736694 481.084198,1073.635864
C537.977600,1208.396118 594.365112,1341.958008 650.639954,1475.253052
C671.545288,1477.250610 850.587708,1476.370483 857.275146,1474.384888
C857.275146,1319.501709 857.275146,1164.707520 857.275146,1009.877747
C820.111389,1009.877747 783.604797,1009.877747 747.287354,1009.877747
C747.018433,1010.861938 746.855103,1011.180908 746.854858,1011.499939
C746.746033,1143.139038 746.648804,1274.778076 746.538818,1406.417236
C746.538269,1407.072876 746.388184,1407.734131 746.252258,1408.381104
C746.194031,1408.657959 746.053467,1409.030518 745.840820,1409.133423
C745.570862,1409.264160 745.189697,1409.165039 744.160767,1409.165039
C713.989868,1337.451416 683.707458,1265.472534 652.718262,1192.136841
M959.001221,1009.216125
C944.459412,1009.216125 929.917542,1009.216125 913.991943,1009.216125
C917.005737,1014.547791 919.083618,1018.292542 921.224487,1022.000916
C985.469849,1133.285889 1049.688477,1244.586670 1114.062378,1355.797241
C1117.536865,1361.799561 1119.156494,1367.707397 1119.095215,1374.644775
C1118.830933,1404.632812 1118.982300,1434.624390 1118.982300,1464.614746
C1118.982300,1468.191528 1118.982178,1471.768188 1118.982178,1475.875977
C1156.657959,1475.942383 1193.117554,1475.921387 1230.004639,1475.903931
C1230.004639,1471.100220 1230.004517,1467.789429 1230.004517,1464.478516
C1230.004517,1434.821411 1230.214233,1405.161865 1229.861328,1375.508911
C1229.770020,1367.838135 1231.788940,1361.477295 1235.588745,1354.913086
C1300.632080,1242.552002 1365.488647,1130.082764 1430.359375,1017.621826
C1431.736206,1015.235107 1432.792358,1012.663391 1434.319092,1009.516113
C1391.183350,1009.516113 1349.285889,1009.516113 1306.819458,1009.516113
C1263.059082,1085.544434 1219.280884,1161.603882 1175.042847,1238.462158
C1172.418457,1234.160156 1170.285034,1230.816528 1168.299927,1227.386841
C1127.610229,1157.086060 1086.880249,1086.808472 1046.377808,1016.399963
C1043.363647,1011.160278 1040.141357,1008.979919 1033.975464,1009.070435
C1009.655273,1009.427551 985.326599,1009.216614 959.001221,1009.216125
z"/>
<path fill="#FEFEFE" opacity="1.000000" stroke="none"
d="
M1542.000000,1475.916992
C1523.089722,1475.916992 1505.179443,1475.916992 1486.763916,1475.916992
C1486.763916,1320.110352 1486.763916,1165.253540 1486.763916,1009.806763
C1556.285645,1009.806763 1625.816528,1009.806763 1696.539429,1009.806763
C1731.653076,1144.159302 1766.868286,1278.899902 1802.083374,1413.640625
C1802.690063,1413.657227 1803.296875,1413.673828 1803.903564,1413.690552
C1838.957642,1279.261963 1874.011719,1144.833252 1909.208252,1009.858276
C1978.869995,1009.858276 2048.318604,1009.858276 2118.277832,1009.858276
C2118.277832,1164.932129 2118.277832,1319.766602 2118.277832,1475.259277
C2081.697754,1475.259277 2045.304443,1475.259277 2008.293945,1475.259277
C2008.293945,1343.435669 2008.293945,1211.892090 2008.293945,1080.348511
C2007.608154,1080.213501 2006.922363,1080.078369 2006.236450,1079.943237
C1996.857056,1112.357666 1989.040771,1145.213989 1980.320435,1177.810669
C1971.464111,1210.916138 1962.983276,1244.122070 1954.337402,1277.283936
C1945.775513,1310.123657 1937.197021,1342.959106 1928.654541,1375.803833
C1920.041992,1408.918823 1911.464722,1442.042969 1902.799194,1475.443481
C1835.939819,1475.443481 1769.731934,1475.443481 1702.546143,1475.443481
C1668.217651,1343.775513 1633.850098,1211.957520 1599.482544,1080.139404
C1598.926636,1080.179199 1598.370850,1080.218994 1597.815063,1080.258789
C1597.815063,1211.767822 1597.815063,1343.276855 1597.815063,1475.916992
C1578.770142,1475.916992 1560.885010,1475.916992 1542.000000,1475.916992
z"/>
<path fill="#FDFDFD" opacity="1.000000" stroke="none"
d="
M653.071655,1192.815308
C683.707458,1265.472534 713.989868,1337.451416 744.160767,1409.165039
C745.189697,1409.165039 745.570862,1409.264160 745.840820,1409.133423
C746.053467,1409.030518 746.194031,1408.657959 746.252258,1408.381104
C746.388184,1407.734131 746.538269,1407.072876 746.538818,1406.417236
C746.648804,1274.778076 746.746033,1143.139038 746.854858,1011.499939
C746.855103,1011.180908 747.018433,1010.861938 747.287354,1009.877747
C783.604797,1009.877747 820.111389,1009.877747 857.275146,1009.877747
C857.275146,1164.707520 857.275146,1319.501709 857.275146,1474.384888
C850.587708,1476.370483 671.545288,1477.250610 650.639954,1475.253052
C594.365112,1341.958008 537.977600,1208.396118 481.084198,1073.635864
C478.380066,1077.736694 479.286682,1081.310669 479.284668,1084.561401
C479.228546,1175.548096 479.239532,1266.534912 479.239471,1357.521606
C479.239441,1393.183105 479.239471,1428.844482 479.239471,1464.505981
C479.239471,1468.072998 479.239441,1471.639893 479.239441,1475.558838
C441.804169,1475.558838 405.586884,1475.558838 368.720612,1475.558838
C368.720612,1320.420776 368.720612,1165.601807 368.720612,1010.721680
C376.362366,1008.593018 540.850159,1007.638428 575.927673,1009.646484
C601.399841,1070.180298 627.059082,1131.158569 653.071655,1192.815308
z"/>
<path fill="#FEFEFE" opacity="1.000000" stroke="none"
d="
M960.001160,1009.216187
C985.326599,1009.216614 1009.655273,1009.427551 1033.975464,1009.070435
C1040.141357,1008.979919 1043.363647,1011.160278 1046.377808,1016.399963
C1086.880249,1086.808472 1127.610229,1157.086060 1168.299927,1227.386841
C1170.285034,1230.816528 1172.418457,1234.160156 1175.042847,1238.462158
C1219.280884,1161.603882 1263.059082,1085.544434 1306.819458,1009.516113
C1349.285889,1009.516113 1391.183350,1009.516113 1434.319092,1009.516113
C1432.792358,1012.663391 1431.736206,1015.235107 1430.359375,1017.621826
C1365.488647,1130.082764 1300.632080,1242.552002 1235.588745,1354.913086
C1231.788940,1361.477295 1229.770020,1367.838135 1229.861328,1375.508911
C1230.214233,1405.161865 1230.004517,1434.821411 1230.004517,1464.478516
C1230.004517,1467.789429 1230.004639,1471.100220 1230.004639,1475.903931
C1193.117554,1475.921387 1156.657959,1475.942383 1118.982178,1475.875977
C1118.982178,1471.768188 1118.982300,1468.191528 1118.982300,1464.614746
C1118.982300,1434.624390 1118.830933,1404.632812 1119.095215,1374.644775
C1119.156494,1367.707397 1117.536865,1361.799561 1114.062378,1355.797241
C1049.688477,1244.586670 985.469849,1133.285889 921.224487,1022.000916
C919.083618,1018.292542 917.005737,1014.547791 913.991943,1009.216125
C929.917542,1009.216125 944.459412,1009.216125 960.001160,1009.216187
z"/>
</svg>

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.
+28
View File
@@ -0,0 +1,28 @@
[package]
name = "nymvpn-cli"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0"
authors = ["Nym Technologies S.A."]
description = "Cli to manage VPN session via nymvpn daemon"
homepage = "https://nymvpn.net"
repository = "https://github.com/nymvpn/nymvpn-app"
[[bin]]
name = "nymvpn"
path = "src/main.rs"
[dependencies]
async-trait = "0.1.68"
clap = { version = "4.2.3", features = ["derive"] }
tokio = { version = "1.27.0", features = ["rt-multi-thread", "macros", "signal"] }
validator = { version = "0.16.0", features = ["derive"] }
nymvpn-controller = {path = "../nymvpn-controller"}
nymvpn-types = {path = "../nymvpn-types"}
thiserror = "1.0.40"
tonic = "0.9.2"
dialoguer = { version = "0.10.4", features = ["fuzzy-select"] }
indicatif = "0.17.3"
tokio-stream = { version = "0.1.12", features = ["sync"] }
console = "0.15.5"
+1
View File
@@ -0,0 +1 @@
../nymvpn.conf.toml
+59
View File
@@ -0,0 +1,59 @@
use std::process::ExitCode;
use async_trait::async_trait;
use clap::{Parser, Subcommand};
use console::style;
use crate::commands::{
connect::Connect, disconnect::Disconnect, error::CliError, locations::ListLocations,
sign_in::SignIn, sign_out::SignOut, status::Status,
};
#[async_trait]
pub trait RunCommand {
async fn run(self) -> Result<(), CliError>;
}
#[derive(Parser, Debug)]
#[command(author, version, about)]
pub struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
/// Sign in to your https://nymvpn.net account
SignIn(SignIn),
/// Sign out current device
SignOut(SignOut),
/// Current VPN status
Status(Status),
/// Available locations for VPN
Locations(ListLocations),
/// Connect VPN
Connect(Connect),
/// Disconnect VPN
Disconnect(Disconnect),
}
impl Cli {
pub async fn run(self) -> ExitCode {
let output = match self.command {
Commands::SignIn(sign_in) => sign_in.run().await,
Commands::SignOut(sign_out) => sign_out.run().await,
Commands::Locations(list_locations) => list_locations.run().await,
Commands::Connect(connect) => connect.run().await,
Commands::Disconnect(disconnect) => disconnect.run().await,
Commands::Status(status) => status.run().await,
};
match output {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("{}", style(e).for_stderr().red());
ExitCode::FAILURE
}
}
}
}
@@ -0,0 +1,156 @@
use std::time::Duration;
use clap::Args;
use console::style;
use dialoguer::{theme::ColorfulTheme, FuzzySelect};
use indicatif::{ProgressBar, ProgressStyle};
use tokio_stream::StreamExt;
use tonic::Request;
use nymvpn_types::{notification::Notification, vpn_session::VpnStatus};
use crate::cli::RunCommand;
use super::{error::CliError, locations::list_locations};
#[derive(Debug, Args)]
pub struct Connect {}
pub async fn start_signal_watch(message: String) {
tokio::spawn(async move {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to install TERM signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
println!("{}", style(message).cyan());
std::process::exit(0);
});
}
#[async_trait::async_trait]
impl RunCommand for Connect {
async fn run(self) -> Result<(), CliError> {
// get locations
let locations = list_locations().await?;
let selection = FuzzySelect::with_theme(&ColorfulTheme::default())
.items(&locations)
.with_prompt("Location:")
.interact_opt()?;
if let Some(index) = selection {
// start signal watcher for user interrupts
start_signal_watch(
"You can continue to watch status using 'nymvpn status' cli or on the app.\nTo end current session use 'nymvpn disconnect' cli or the app".into(),
)
.await;
let location = locations.get(index).unwrap();
let mut client = nymvpn_controller::new_grpc_client()
.await
.map_err(|_| CliError::DaemonUnavailable)?;
let mut stream = client.watch_events(()).await?.into_inner();
let vpn_status = client
.connect_vpn(Request::new(location.clone().into()))
.await
.map(|res| res.into_inner())
.map(VpnStatus::from)?;
// wait while vpn becomes active
let pb = ProgressBar::new(100);
let mut progress = 0;
let mut done = false;
pb.set_style(
ProgressStyle::with_template(
"{spinner:.blue} [{elapsed_precise}] [{bar:.cyan/blue}] {wide_msg}",
)
.unwrap(),
);
pb.set_position(progress);
pb.set_message(format!("{}", style(vpn_status.to_string()).yellow()));
pb.enable_steady_tick(Duration::from_secs(1));
while let Some(event) = stream.next().await {
match event {
Ok(event) => {
if let Some(event) = event.event {
match event {
nymvpn_controller::proto::daemon_event::Event::VpnStatus(
vpn_status,
) => {
let vpn_status: VpnStatus = vpn_status.into();
progress = match vpn_status {
VpnStatus::Accepted(_) => 25,
VpnStatus::Connected(_, _) => {
done = true;
100
}
VpnStatus::Connecting(_) => 95,
VpnStatus::Disconnected => {
done = true;
0
}
VpnStatus::Disconnecting(_) => progress,
VpnStatus::ServerCreated(_) => 50,
VpnStatus::ServerRunning(_) => 75,
VpnStatus::ServerReady(_) => 80,
};
pb.set_position(progress);
pb.set_message(format!(
"{}",
style(vpn_status.to_string()).yellow()
));
}
nymvpn_controller::proto::daemon_event::Event::Notification(
notification,
) => {
let id = notification.id.clone();
if let Ok(notification) = Notification::try_from(notification) {
pb.set_position(0);
pb.set_message(format!(
"{}",
style(notification.message).red(),
));
}
client.ack_notification(id).await?;
done = true;
}
}
}
}
Err(err) => Err(err)?,
}
if done {
break;
}
}
pb.finish();
}
Ok(())
}
}
@@ -0,0 +1,28 @@
use clap::Args;
use console::style;
use crate::cli::RunCommand;
use super::error::CliError;
#[derive(Debug, Args)]
pub struct Disconnect {}
#[async_trait::async_trait]
impl RunCommand for Disconnect {
async fn run(self) -> Result<(), CliError> {
let mut client = nymvpn_controller::new_grpc_client()
.await
.map_err(|_| CliError::DaemonUnavailable)?;
let vpn_status = client
.disconnect_vpn(())
.await
.map(|res| res.into_inner())
.map(nymvpn_types::vpn_session::VpnStatus::from)?;
println!("{}", style(vpn_status).yellow());
Ok(())
}
}
@@ -0,0 +1,13 @@
use tonic::Status;
#[derive(Debug, thiserror::Error)]
pub enum CliError {
#[error("daemon is offline")]
DaemonUnavailable,
#[error("{}", .0.message())]
Grpc(#[from] Status),
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
InvalidArgument(String),
}
@@ -0,0 +1,47 @@
use async_trait::async_trait;
use clap::Args;
use console::style;
use validator::Validate;
use crate::cli::RunCommand;
use super::error::CliError;
#[derive(Args, Debug, Validate)]
pub struct ListLocations {}
pub async fn list_locations() -> Result<Vec<nymvpn_types::location::Location>, CliError> {
let mut client = nymvpn_controller::new_grpc_client()
.await
.map_err(|_| CliError::DaemonUnavailable)?;
let locations = client.get_locations(()).await?;
Ok(locations.into_inner().into())
}
#[async_trait]
impl RunCommand for ListLocations {
async fn run(self) -> Result<(), CliError> {
let locations = list_locations().await?;
for location in locations {
if location.state.is_some() {
println!(
"{}, {}, {}",
style(location.city).white(),
style(location.state.unwrap()).white().dim(),
style(location.country).white().dim()
);
} else {
println!(
"{}, {}",
style(location.city).white(),
style(location.country).white().dim()
);
}
}
Ok(())
}
}
@@ -0,0 +1,7 @@
pub mod connect;
pub mod disconnect;
pub mod error;
pub mod locations;
pub mod sign_in;
pub mod sign_out;
pub mod status;
@@ -0,0 +1,49 @@
use async_trait::async_trait;
use clap::Args;
use console::style;
use dialoguer::{theme::ColorfulTheme, Input, Password};
use nymvpn_controller::proto::SignInRequest;
use validator::Validate;
use crate::cli::RunCommand;
use super::error::CliError;
#[derive(Args, Debug, Validate)]
pub struct SignIn {
email: Option<String>,
}
#[async_trait]
impl RunCommand for SignIn {
async fn run(self) -> Result<(), CliError> {
let email = match self.email {
Some(email) => email,
None => Input::<String>::with_theme(&ColorfulTheme::default())
.with_prompt("Email:")
.interact_text()?,
};
if !validator::validate_email(&email) {
return Err(CliError::InvalidArgument(format!(
"\"{email}\" is not a valid email"
)));
}
let password = Password::with_theme(&ColorfulTheme::default())
.with_prompt("Password:")
.interact()?;
let mut client = nymvpn_controller::new_grpc_client()
.await
.map_err(|_| CliError::DaemonUnavailable)?;
client
.account_sign_in(SignInRequest { email, password })
.await?;
println!("{}", style("Successfully signed in").yellow());
Ok(())
}
}
@@ -0,0 +1,25 @@
use async_trait::async_trait;
use clap::Args;
use console::style;
use crate::cli::RunCommand;
use super::error::CliError;
#[derive(Args, Debug)]
pub struct SignOut;
#[async_trait]
impl RunCommand for SignOut {
async fn run(self) -> Result<(), CliError> {
let mut client = nymvpn_controller::new_grpc_client()
.await
.map_err(|_| CliError::DaemonUnavailable)?;
client.account_sign_out(()).await?;
println!("{}", style("Successfully signed out").yellow());
Ok(())
}
}
@@ -0,0 +1,28 @@
use clap::Args;
use console::style;
use crate::cli::RunCommand;
use super::error::CliError;
#[derive(Debug, Args)]
pub struct Status {}
#[async_trait::async_trait]
impl RunCommand for Status {
async fn run(self) -> Result<(), CliError> {
let mut client = nymvpn_controller::new_grpc_client()
.await
.map_err(|_| CliError::DaemonUnavailable)?;
let vpn_status = client
.get_vpn_status(())
.await
.map(|res| res.into_inner())
.map(nymvpn_types::vpn_session::VpnStatus::from)?;
println!("{}", style(vpn_status).yellow());
Ok(())
}
}
+10
View File
@@ -0,0 +1,10 @@
pub mod cli;
pub mod commands;
use std::process::ExitCode;
use clap::Parser;
#[tokio::main]
async fn main() -> ExitCode {
cli::Cli::parse().run().await
}
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "nymvpn-config"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0"
authors = ["Nym Technologies S.A."]
homepage = "https://nymvpn.net"
repository = "https://github.com/nymvpn/nymvpn-app"
[dependencies]
figment = { version = "0.10.8", features = ["env", "toml"] }
once_cell = "1.17.1"
serde = { version = "1.0.160", features = ["derive"] }
thiserror = "1.0.40"
tokio = { version = "1.27.0", features = ["fs"] }
[build-dependencies]
toml = "0.7.3"
serde = "1.0.160"
+25
View File
@@ -0,0 +1,25 @@
use std::error::Error;
use serde::Deserialize;
#[derive(Deserialize)]
struct Package {
version: String,
}
#[derive(Deserialize)]
struct CargoToml {
package: Package,
}
fn main() -> Result<(), Box<dyn Error>> {
let path = "../nymvpn-packages/Cargo.toml";
println!("cargo:rerun-if-changed={path}");
let cargo_toml: CargoToml = toml::from_str(&std::fs::read_to_string(path)?)?;
println!(
"cargo:rustc-env=NYMVPN_VERSION={}",
cargo_toml.package.version
);
Ok(())
}
+160
View File
@@ -0,0 +1,160 @@
#[cfg(unix)]
use std::str::FromStr;
use std::{
net::{IpAddr, Ipv4Addr},
path::{Path, PathBuf},
};
use figment::{
providers::{Env, Format, Serialized, Toml},
Figment,
};
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("failed to create dir {0}: {1}")]
CreateDirError(PathBuf, std::io::Error),
#[error("failed to set permissions ({0:?}) on ({1}): {2:?}")]
DirPermissionError(std::fs::Permissions, PathBuf, std::io::Error),
}
static CONFIG: OnceCell<Config> = OnceCell::new();
static CONFIG_DIR: OnceCell<PathBuf> = OnceCell::new();
static LOG_DIR: OnceCell<PathBuf> = OnceCell::new();
static SOCKET_PATH: OnceCell<PathBuf> = OnceCell::new();
const CONFIG_FILENAME: &str = "nymvpn.conf.toml";
pub fn config() -> &'static Config {
#[cfg(windows)]
let program_data_path = PathBuf::from(std::env::var("ProgramData").unwrap_or(
std::env::var("PROGRAMDATA").expect("missing ProgramData and PROGRAMDATA env var"),
));
let config_dir = CONFIG_DIR.get_or_init(|| {
#[cfg(unix)]
return PathBuf::from("/etc/nymvpn");
#[cfg(windows)]
{
return program_data_path.join("nymvpn");
}
});
let _ = LOG_DIR.get_or_init(|| {
#[cfg(unix)]
return PathBuf::from("/var/log/nymvpn");
#[cfg(windows)]
return program_data_path.join("nymvpn").join("log");
});
let _ = SOCKET_PATH.get_or_init(|| {
#[cfg(unix)]
return PathBuf::from("/var/run/nymvpn.sock");
#[cfg(windows)]
return PathBuf::from("//./pipe/nymvpn");
});
CONFIG.get_or_init(|| {
Figment::from(Serialized::defaults(Config::default()))
.merge(Toml::file(PathBuf::from(config_dir).join(CONFIG_FILENAME)))
.merge(Toml::file(CONFIG_FILENAME))
.merge(Env::prefixed("NYMVPN_"))
.extract()
.unwrap()
})
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
config_dir: PathBuf,
log_dir: PathBuf,
// todo: non string types for grpc and rest api?
grpc_api_host_port: String,
socket_path: PathBuf,
daemon_log_filename: String,
allowed_endpoint_ipv4: IpAddr,
license_file_path: Option<PathBuf>,
}
impl Default for Config {
fn default() -> Self {
Self {
config_dir: CONFIG_DIR.get().unwrap().into(),
log_dir: LOG_DIR.get().unwrap().into(),
grpc_api_host_port: "grpcs://api.nymvpn.net:44444".into(),
socket_path: SOCKET_PATH.get().unwrap().into(),
daemon_log_filename: "nymvpn-daemon.log".into(),
// IP of api.nymvpn.net
allowed_endpoint_ipv4: IpAddr::V4(Ipv4Addr::new(168, 220, 80, 137)),
license_file_path: None,
}
}
}
impl Config {
pub fn db_dir(&self) -> PathBuf {
self.config_dir.join("db")
}
pub fn db_url(&self) -> String {
format!("sqlite://{}/nymvpn.db?mode=rwc", self.db_dir().display())
}
pub fn grpc_api_host_port(&self) -> &str {
&self.grpc_api_host_port
}
pub fn allowed_endpoint_ipv4(&self) -> &IpAddr {
&self.allowed_endpoint_ipv4
}
pub fn log_dir(&self) -> &Path {
self.log_dir.as_path()
}
pub fn daemon_log_filename(&self) -> &str {
&self.daemon_log_filename
}
pub fn daemon_log_file_full_path(&self) -> PathBuf {
self.log_dir().join(self.daemon_log_filename())
}
pub fn socket_path(&self) -> &Path {
return &self.socket_path;
}
pub fn version(&self) -> &'static str {
env!("NYMVPN_VERSION")
}
pub fn license_file_path(&self) -> PathBuf {
if self.license_file_path.is_some() {
return self.license_file_path.clone().unwrap();
}
#[cfg(target_os = "linux")]
return PathBuf::from_str("/opt/nymvpn/nymvpn-oss-licenses.html").unwrap();
#[cfg(target_os = "macos")]
return PathBuf::from_str(
"/Applications/nymvpn.net/Contents/Resources/nymvpn-oss-licenses.html",
)
.unwrap();
#[cfg(target_os = "windows")]
return PathBuf::from(std::env::var("PROGRAMFILES").unwrap_or(
std::env::var("ProgramFiles").expect("missing PROGRAMFILES and ProgramFiles env var"),
))
.join("nymvpn")
.join("nymvpn-oss-licenses.html");
}
pub fn icon_path(&self) -> &'static str {
#[cfg(target_os = "linux")]
return "/usr/share/icons/hicolor/32x32/apps/nymvpn.png";
#[cfg(target_os = "macos")]
return "/Applications/nymvpn.net/Contents/Resources/icon.icns";
#[cfg(target_os = "windows")]
return "";
}
}
@@ -0,0 +1,26 @@
[package]
name = "nymvpn-controller"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0"
authors = ["Nym Technologies S.A."]
homepage = "https://nymvpn.net"
repository = "https://github.com/nymvpn/nymvpn-app"
[dependencies]
futures = "0.3.28"
hyper = "0.14.26"
parity-tokio-ipc = "0.9.0"
prost = "0.11.9"
prost-types = "0.11.9"
thiserror = "1.0.40"
tokio = "1.27.0"
tonic = "0.9.2"
tower = "0.4.13"
nymvpn-config = { path = "../nymvpn-config" }
nymvpn-types = {path = "../nymvpn-types"} # grpc types to nymvpn-types conversions
chrono = { version = "0.4.24", features = ["serde"] }
[build-dependencies]
tonic-build = "0.9.2"
@@ -0,0 +1,7 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
const PROTO_PATH: &str = "proto/nymvpn-controller.proto";
tonic_build::configure().protoc_arg("--experimental_allow_proto3_optional")
.compile(&[PROTO_PATH], &["proto"])?;
println!("cargo:rerun-if-changed=proto");
Ok(())
}
@@ -0,0 +1,126 @@
syntax = "proto3";
package nymvpn.controller;
import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/timestamp.proto";
service ControllerService {
// Locations served
rpc GetLocations(google.protobuf.Empty) returns (Locations);
rpc RecentLocations(google.protobuf.Empty) returns (Locations);
// Account
rpc IsAuthenticated(google.protobuf.Empty) returns (google.protobuf.BoolValue);
rpc AccountSignIn(SignInRequest) returns (google.protobuf.Empty);
rpc AccountSignOut(google.protobuf.Empty) returns (google.protobuf.Empty);
rpc GetAccountInfo(google.protobuf.Empty) returns (AccountInfo);
// Control VPN
rpc ConnectVpn(Location) returns (VpnStatus);
rpc DisconnectVpn(google.protobuf.Empty) returns (VpnStatus);
rpc GetVpnStatus(google.protobuf.Empty) returns (VpnStatus);
// Notifications
rpc GetNotifications(google.protobuf.Empty) returns (Notifications);
rpc AckNotification(google.protobuf.StringValue) returns (google.protobuf.Empty);
// Versions and Updates
rpc LatestAppVersion(google.protobuf.Empty) returns (google.protobuf.StringValue);
// Events
rpc WatchEvents(google.protobuf.Empty) returns (stream DaemonEvent);
}
message SignInRequest {
string email = 1;
string password = 2;
}
message AccountInfo {
string email = 1;
uint32 balance = 2;
}
message Notifications {
repeated Notification notification = 1;
}
enum NotificationType {
SERVER_FAILED = 0;
CLIENT_FAILED = 1;
}
message Notification {
string id = 1;
NotificationType notification_type = 2;
string message = 3;
google.protobuf.Timestamp timestamp = 4;
}
message VpnStatus {
message Accepted {
Location location = 1;
};
message ServerCreated {
Location location = 1;
}
message ServerRunning {
Location location = 1;
}
message ServerReady {
Location location = 1;
}
message Connecting {
Location location = 1;
}
message Connected {
Location location = 1;
google.protobuf.Timestamp timestamp = 2;
}
message Disconnecting {
Location location = 1;
}
message Disconnected {}
oneof vpn_status {
Accepted accepted = 1;
Connecting connecting = 2;
ServerCreated server_created = 3;
ServerRunning server_running = 4;
ServerReady server_ready = 5;
Connected connected = 6;
Disconnecting disconnecting = 7;
Disconnected disconnected = 8;
}
}
message Locations {
repeated Location location = 1;
}
message Location {
string code = 1;
string country = 2;
string country_code = 3;
string city = 4;
string city_code = 5;
optional string state = 6;
optional string state_code = 7;
}
message DaemonEvent {
oneof event {
VpnStatus vpn_status = 1;
Notification notification = 2;
}
}
@@ -0,0 +1,76 @@
use std::task::{Context, Poll};
use hyper::Body;
use tonic::body::BoxBody;
use tower::{Layer, Service};
#[tonic::async_trait]
pub trait Auth: Clone + Send + Sync {
async fn is_authenticated(&self) -> bool;
}
#[derive(Debug, Clone)]
pub struct ControllerAuthLayer<P: Auth> {
auth: P,
}
impl<P: Auth> ControllerAuthLayer<P> {
pub fn new(auth: P) -> Self {
Self { auth }
}
}
const ALLOWED_UNAUTHORIZED_PATHS: [&str; 2] = [
"/nymvpn.controller.ControllerService/AccountSignIn",
"/nymvpn.controller.ControllerService/IsAuthenticated",
];
#[derive(Debug, Clone)]
pub struct ControllerAuthMiddleware<S, P: Auth> {
auth: P,
inner: S,
}
impl<S, P: Auth> Layer<S> for ControllerAuthLayer<P> {
type Service = ControllerAuthMiddleware<S, P>;
fn layer(&self, inner: S) -> Self::Service {
ControllerAuthMiddleware {
auth: self.auth.clone(),
inner,
}
}
}
impl<S, P> Service<hyper::Request<Body>> for ControllerAuthMiddleware<S, P>
where
S: Service<hyper::Request<Body>, Response = hyper::Response<BoxBody>> + Clone + Send + 'static,
S::Future: Send + 'static,
P: Auth + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = futures::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: hyper::Request<Body>) -> Self::Future {
// This is necessary because tonic internally uses `tower::buffer::Buffer`.
// See https://github.com/tower-rs/tower/issues/547#issuecomment-767629149
// for details on why this is necessary
let clone = self.inner.clone();
let mut inner = std::mem::replace(&mut self.inner, clone);
let auth = self.auth.clone();
Box::pin(async move {
if ALLOWED_UNAUTHORIZED_PATHS.contains(&req.uri().path())
|| auth.is_authenticated().await
{
return inner.call(req).await;
}
Ok(tonic::Status::unauthenticated("please sign in first").to_http())
})
}
}
@@ -0,0 +1,8 @@
impl From<crate::proto::SignInRequest> for nymvpn_types::nymvpn_server::UserCredentials {
fn from(value: crate::proto::SignInRequest) -> Self {
Self {
email: value.email,
password: value.password,
}
}
}
@@ -0,0 +1,48 @@
impl From<nymvpn_types::location::Location> for crate::proto::Location {
fn from(value: nymvpn_types::location::Location) -> Self {
Self {
code: value.code,
country: value.country,
country_code: value.country_code,
city: value.city,
city_code: value.city_code,
state: value.state,
state_code: value.state_code,
}
}
}
impl From<crate::proto::Location> for nymvpn_types::location::Location {
fn from(value: crate::proto::Location) -> Self {
Self {
code: value.code,
country: value.country,
country_code: value.country_code,
city: value.city,
city_code: value.city_code,
state: value.state,
state_code: value.state_code,
}
}
}
impl From<Vec<nymvpn_types::location::Location>> for crate::proto::Locations {
fn from(value: Vec<nymvpn_types::location::Location>) -> Self {
Self {
location: value
.into_iter()
.map(crate::proto::Location::from)
.collect(),
}
}
}
impl From<crate::proto::Locations> for Vec<nymvpn_types::location::Location> {
fn from(value: crate::proto::Locations) -> Self {
value
.location
.into_iter()
.map(nymvpn_types::location::Location::from)
.collect()
}
}
@@ -0,0 +1,4 @@
pub mod account;
pub mod location;
pub mod notification;
pub mod vpn_status;
@@ -0,0 +1,77 @@
use crate::timestamp_to_datetime_utc;
impl From<crate::proto::NotificationType> for nymvpn_types::notification::NotificationType {
fn from(value: crate::proto::NotificationType) -> Self {
match value {
crate::proto::NotificationType::ServerFailed => {
nymvpn_types::notification::NotificationType::ServerFailed
}
crate::proto::NotificationType::ClientFailed => {
nymvpn_types::notification::NotificationType::ClientFailed
}
}
}
}
impl From<nymvpn_types::notification::NotificationType> for crate::proto::NotificationType {
fn from(value: nymvpn_types::notification::NotificationType) -> Self {
match value {
nymvpn_types::notification::NotificationType::ServerFailed => {
crate::proto::NotificationType::ServerFailed
}
nymvpn_types::notification::NotificationType::ClientFailed => {
crate::proto::NotificationType::ClientFailed
}
}
}
}
impl TryFrom<crate::proto::Notification> for nymvpn_types::notification::Notification {
type Error = String;
fn try_from(value: crate::proto::Notification) -> Result<Self, Self::Error> {
Ok(Self {
id: value.id,
message: value.message,
notification_type: value.notification_type.try_into()?,
timestamp: timestamp_to_datetime_utc(value.timestamp)?,
})
}
}
impl From<nymvpn_types::notification::Notification> for crate::proto::Notification {
fn from(value: nymvpn_types::notification::Notification) -> Self {
let seconds = value.timestamp.timestamp();
let nanos = value.timestamp.timestamp_subsec_nanos();
Self {
id: value.id,
notification_type: value.notification_type.into(),
message: value.message,
timestamp: Some(prost_types::Timestamp {
seconds,
nanos: nanos as i32,
}),
}
}
}
impl From<Vec<nymvpn_types::notification::Notification>> for crate::proto::Notifications {
fn from(value: Vec<nymvpn_types::notification::Notification>) -> Self {
Self {
notification: value
.into_iter()
.map(crate::proto::Notification::from)
.collect(),
}
}
}
impl TryFrom<crate::proto::Notifications> for Vec<nymvpn_types::notification::Notification> {
type Error = String;
fn try_from(value: crate::proto::Notifications) -> Result<Self, Self::Error> {
let mut notifications = vec![];
for notification in value.notification {
notifications.push(notification.try_into()?)
}
Ok(notifications)
}
}
@@ -0,0 +1,110 @@
use crate::{datetime_utc_to_timestamp, timestamp_to_datetime_utc};
impl From<nymvpn_types::vpn_session::VpnStatus> for crate::proto::VpnStatus {
fn from(value: nymvpn_types::vpn_session::VpnStatus) -> Self {
match value {
nymvpn_types::vpn_session::VpnStatus::Accepted(location) => crate::proto::VpnStatus {
vpn_status: Some(crate::proto::vpn_status::VpnStatus::Accepted(
crate::proto::vpn_status::Accepted {
location: Some(location.into()),
},
)),
},
nymvpn_types::vpn_session::VpnStatus::Connecting(location) => crate::proto::VpnStatus {
vpn_status: Some(crate::proto::vpn_status::VpnStatus::Connecting(
crate::proto::vpn_status::Connecting {
location: Some(location.into()),
},
)),
},
nymvpn_types::vpn_session::VpnStatus::ServerRunning(location) => {
crate::proto::VpnStatus {
vpn_status: Some(crate::proto::vpn_status::VpnStatus::ServerRunning(
crate::proto::vpn_status::ServerRunning {
location: Some(location.into()),
},
)),
}
}
nymvpn_types::vpn_session::VpnStatus::ServerReady(location) => crate::proto::VpnStatus {
vpn_status: Some(crate::proto::vpn_status::VpnStatus::ServerReady(
crate::proto::vpn_status::ServerReady {
location: Some(location.into()),
},
)),
},
nymvpn_types::vpn_session::VpnStatus::Connected(location, connected_time) => {
crate::proto::VpnStatus {
vpn_status: Some(crate::proto::vpn_status::VpnStatus::Connected(
crate::proto::vpn_status::Connected {
location: Some(location.into()),
timestamp: Some(datetime_utc_to_timestamp(connected_time)),
},
)),
}
}
nymvpn_types::vpn_session::VpnStatus::Disconnecting(location) => {
crate::proto::VpnStatus {
vpn_status: Some(crate::proto::vpn_status::VpnStatus::Disconnecting(
crate::proto::vpn_status::Disconnecting {
location: Some(location.into()),
},
)),
}
}
nymvpn_types::vpn_session::VpnStatus::Disconnected => crate::proto::VpnStatus {
vpn_status: Some(crate::proto::vpn_status::VpnStatus::Disconnected(
crate::proto::vpn_status::Disconnected {},
)),
},
nymvpn_types::vpn_session::VpnStatus::ServerCreated(location) => {
crate::proto::VpnStatus {
vpn_status: Some(crate::proto::vpn_status::VpnStatus::ServerCreated(
crate::proto::vpn_status::ServerCreated {
location: Some(location.into()),
},
)),
}
}
}
}
}
impl From<crate::proto::VpnStatus> for nymvpn_types::vpn_session::VpnStatus {
fn from(value: crate::proto::VpnStatus) -> Self {
let vpn_status = value.vpn_status.unwrap();
match vpn_status {
crate::proto::vpn_status::VpnStatus::Accepted(accepted) => {
nymvpn_types::vpn_session::VpnStatus::Accepted(accepted.location.unwrap().into())
}
crate::proto::vpn_status::VpnStatus::Connecting(connecting) => {
nymvpn_types::vpn_session::VpnStatus::Connecting(connecting.location.unwrap().into())
}
crate::proto::vpn_status::VpnStatus::ServerRunning(srun) => {
nymvpn_types::vpn_session::VpnStatus::ServerRunning(srun.location.unwrap().into())
}
crate::proto::vpn_status::VpnStatus::ServerReady(sr) => {
nymvpn_types::vpn_session::VpnStatus::ServerReady(sr.location.unwrap().into())
}
crate::proto::vpn_status::VpnStatus::Connected(connected) => {
nymvpn_types::vpn_session::VpnStatus::Connected(
connected.location.unwrap().into(),
timestamp_to_datetime_utc(connected.timestamp).unwrap(),
)
}
crate::proto::vpn_status::VpnStatus::Disconnecting(disconnecting) => {
nymvpn_types::vpn_session::VpnStatus::Disconnecting(
disconnecting.location.unwrap().into(),
)
}
crate::proto::vpn_status::VpnStatus::Disconnected(_) => {
nymvpn_types::vpn_session::VpnStatus::Disconnected
}
crate::proto::vpn_status::VpnStatus::ServerCreated(server_created) => {
nymvpn_types::vpn_session::VpnStatus::ServerCreated(
server_created.location.unwrap().into(),
)
}
}
}
}
@@ -0,0 +1,153 @@
use std::future::Future;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use auth::Auth;
use parity_tokio_ipc::Endpoint as IpcEndpoint;
use prost_types::Timestamp;
use thiserror::Error;
use tokio::io::AsyncRead;
use tokio::io::AsyncWrite;
use tokio::io::ReadBuf;
use tonic::transport::server::Connected;
use tonic::transport::Channel;
use tonic::transport::Endpoint as TonicEndpoint;
use tonic::transport::Server;
use tonic::transport::Uri;
use tower::service_fn;
pub mod proto {
tonic::include_proto!("nymvpn.controller");
}
pub mod auth;
pub mod conversions;
use chrono::{TimeZone, Utc};
pub use proto::controller_service_server::{ControllerService, ControllerServiceServer};
use nymvpn_types::DateTimeUtc;
use crate::auth::ControllerAuthLayer;
pub type ControllerServiceClient =
proto::controller_service_client::ControllerServiceClient<Channel>;
pub type GrpcServerJoinHandle = tokio::task::JoinHandle<Result<(), ControllerError>>;
#[derive(Debug, Error)]
pub enum ControllerError {
#[error("{0}")]
TonicTransportError(tonic::transport::Error),
#[error("security attributes error {0:#?}")]
SecurityAttributesError(std::io::Error),
#[error("incoming connection error {0:#?}")]
IncomingConnectionError(std::io::Error),
}
pub async fn new_grpc_client() -> Result<ControllerServiceClient, ControllerError> {
let ipc_path = nymvpn_config::config().socket_path();
// URI is unused
let channel = TonicEndpoint::from_static("http://[::]:50051")
.connect_with_connector(service_fn(move |_: Uri| {
IpcEndpoint::connect(ipc_path.clone())
}))
.await
.map_err(ControllerError::TonicTransportError)?;
Ok(ControllerServiceClient::new(channel))
}
pub async fn spawn_grpc_server<S, P, F>(
service: S,
auth: P,
shutdown: F,
) -> std::result::Result<GrpcServerJoinHandle, ControllerError>
where
S: proto::controller_service_server::ControllerService,
F: Future<Output = ()> + Send + 'static,
P: Auth + 'static,
{
use futures::stream::TryStreamExt;
use parity_tokio_ipc::SecurityAttributes;
let socket_path = nymvpn_config::config().socket_path();
let mut endpoint = IpcEndpoint::new(socket_path.to_string_lossy().to_string());
endpoint.set_security_attributes(
SecurityAttributes::allow_everyone_create()
.map_err(ControllerError::SecurityAttributesError)?
.set_mode(0o766)
.map_err(ControllerError::SecurityAttributesError)?,
);
let incoming = endpoint
.incoming()
.map_err(ControllerError::IncomingConnectionError)?;
Ok(tokio::spawn(async move {
Server::builder()
.layer(ControllerAuthLayer::new(auth))
.add_service(ControllerServiceServer::new(service))
.serve_with_incoming_shutdown(incoming.map_ok(StreamBox), shutdown)
.await
.map_err(ControllerError::TonicTransportError)
}))
}
#[derive(Debug)]
struct StreamBox<T: AsyncRead + AsyncWrite>(pub T);
impl<T: AsyncRead + AsyncWrite> Connected for StreamBox<T> {
type ConnectInfo = Option<()>;
fn connect_info(&self) -> Self::ConnectInfo {
None
}
}
impl<T: AsyncRead + AsyncWrite + Unpin> AsyncRead for StreamBox<T> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.0).poll_read(cx, buf)
}
}
impl<T: AsyncRead + AsyncWrite + Unpin> AsyncWrite for StreamBox<T> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
Pin::new(&mut self.0).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.0).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.0).poll_shutdown(cx)
}
}
pub fn timestamp_to_datetime_utc(timestamp: Option<Timestamp>) -> Result<DateTimeUtc, String> {
let timestamp = timestamp.ok_or(format!("no timestamp"))?;
let date_time_utc = match Utc.timestamp_opt(timestamp.seconds, timestamp.nanos as u32) {
chrono::LocalResult::Single(dtu) => dtu,
chrono::LocalResult::None => Err("invalid utc time none")?,
chrono::LocalResult::Ambiguous(a, _b) => {
//Err(format!("ambiguous utc time {a} {b}"))?
a
}
};
Ok(date_time_utc)
}
pub fn datetime_utc_to_timestamp(datetime_utc: DateTimeUtc) -> Timestamp {
let seconds = datetime_utc.timestamp();
let nanos = std::cmp::max(datetime_utc.timestamp_subsec_nanos() as i32, 0);
prost_types::Timestamp { seconds, nanos }
}
+57
View File
@@ -0,0 +1,57 @@
[package]
name = "nymvpn-daemon"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0"
authors = ["Nym Technologies S.A."]
homepage = "https://nymvpn.net"
repository = "https://github.com/nymvpn/nymvpn-app"
[dependencies]
clap = "4.2.7"
async-trait = "0.1.68"
futures = "0.3.28"
hyper = "0.14.26"
thiserror = "1.0.40"
tokio = { version = "1.27.0", features = ["rt-multi-thread", "signal", "macros", "sync", "fs"] }
tokio-stream = { version = "0.1.12", features = ["sync"] }
tonic = "0.9.2"
tower = "0.4.13"
tracing = "0.1.37"
tracing-appender = "0.2.2"
tracing-subscriber = { version = "0.3.16", features = ["default", "env-filter", "tracing-log"] }
nymvpn-config = {path = "../nymvpn-config"}
nymvpn-controller = {path = "../nymvpn-controller"}
nymvpn-server = {path = "../nymvpn-server"}
nymvpn-types = {path = "../nymvpn-types"}
nymvpn-entity = {path = "../nymvpn-entity"}
nymvpn-migration = {path = "../nymvpn-migration"}
sea-orm = { version = "0.11.2", features = ["sqlx-sqlite", "runtime-tokio-rustls"] }
chrono = "0.4.24"
talpid-core = {git = "https://github.com/upvpn/mullvadvpn-app.git", rev = "2023.3.upvpn"}
talpid-types = {git = "https://github.com/upvpn/mullvadvpn-app.git", rev = "2023.3.upvpn"}
talpid-platform-metadata = {git = "https://github.com/upvpn/mullvadvpn-app.git", rev = "2023.3.upvpn"}
futures-channel = "0.3.28"
uuid = { version = "1.3.1", features = ["v4", "serde"] }
libc = "0.2"
lazy_static = "1.0"
[target.'cfg(unix)'.dependencies]
nix = "0.26.2"
[target.'cfg(windows)'.dependencies]
windows-service = "0.6.0"
[target.'cfg(windows)'.dependencies.windows-sys]
version = "0.45.0"
features = [
"Win32_Foundation",
"Win32_Security",
"Win32_Security_Authorization",
"Win32_Security_Authentication_Identity",
"Win32_System_Diagnostics_Debug",
"Win32_System_Kernel",
"Win32_System_Memory",
"Win32_System_Threading",
]
+1
View File
@@ -0,0 +1 @@
../nymvpn.conf.toml
@@ -0,0 +1,10 @@
pub async fn remove_old_socket_file() {
if let Err(e) = tokio::fs::remove_file(nymvpn_config::config().socket_path()).await {
if e.kind() != std::io::ErrorKind::NotFound {
tracing::error!(
"Failed to remove old socket file {}",
nymvpn_config::config().socket_path().display()
);
}
}
}
@@ -0,0 +1,297 @@
use std::sync::Arc;
use tokio::{sync::oneshot, task::JoinHandle};
use tokio_stream::wrappers::UnboundedReceiverStream;
use tonic::{Request, Response, Status};
use nymvpn_controller::{
proto::{AccountInfo, Locations, Notifications, SignInRequest, VpnStatus},
spawn_grpc_server, ControllerError, ControllerService,
};
use nymvpn_migration::DbErr;
use nymvpn_types::notification::Notification;
use crate::{
daemon::{DaemonCommand, DaemonCommandSender, DaemonError, EventListener},
device::handler::DeviceHandler,
shutdown::ShutdownManager,
};
pub struct ControllerServer;
impl ControllerServer {
pub async fn start(
daemon_command_sender: DaemonCommandSender,
shutdown_manager: &ShutdownManager,
device_handler: DeviceHandler,
) -> Result<ControllerServerAndEventBroadcaster, ControllerError> {
let events_subscribers =
Arc::<tokio::sync::RwLock<Vec<DaemonEventsListenerSender>>>::default();
let controller_service = ControllerServiceImpl {
daemon_command_sender,
events_subscribers: events_subscribers.clone(),
};
let handle = spawn_grpc_server(
controller_service,
device_handler,
shutdown_manager.shutdown_received_future(),
)
.await?;
let controller_server_handle = tokio::spawn(async move {
if let Err(e) = handle.await {
tracing::error!("Controller GRPC server error: {e}")
}
tracing::info!("Controller server shut down")
});
Ok(ControllerServerAndEventBroadcaster {
events_subscribers,
controller_server_handle,
})
}
}
pub struct ControllerServerAndEventBroadcaster {
pub events_subscribers: Arc<tokio::sync::RwLock<Vec<DaemonEventsListenerSender>>>,
pub controller_server_handle: JoinHandle<()>,
}
impl ControllerServerAndEventBroadcaster {
async fn notify(&self, event: nymvpn_controller::proto::DaemonEvent) {
let mut subscribers = self.events_subscribers.write().await;
subscribers.retain(|tx| tx.send(Ok(event.clone())).is_ok());
}
}
#[async_trait::async_trait]
impl EventListener for ControllerServerAndEventBroadcaster {
async fn send_vpn_status(&self, status: nymvpn_types::vpn_session::VpnStatus) {
tracing::debug!("notifying new vpn status");
self.notify(nymvpn_controller::proto::DaemonEvent {
event: Some(nymvpn_controller::proto::daemon_event::Event::VpnStatus(
nymvpn_controller::proto::VpnStatus::from(status),
)),
})
.await
}
async fn send_notification(&self, notification: Notification) {
tracing::debug!("sending new notification");
self.notify(nymvpn_controller::proto::DaemonEvent {
event: Some(nymvpn_controller::proto::daemon_event::Event::Notification(
nymvpn_controller::proto::Notification::from(notification),
)),
})
.await
}
}
pub struct ControllerServiceImpl {
daemon_command_sender: DaemonCommandSender,
events_subscribers: Arc<tokio::sync::RwLock<Vec<DaemonEventsListenerSender>>>,
}
pub type ServiceResult<T> = std::result::Result<Response<T>, Status>;
pub type VpnStatusListenerSender =
tokio::sync::mpsc::UnboundedSender<Result<nymvpn_controller::proto::VpnStatus, Status>>;
pub type VpnStatusListenerReceiver =
UnboundedReceiverStream<Result<nymvpn_controller::proto::VpnStatus, Status>>;
pub type DaemonEventsListenerSender =
tokio::sync::mpsc::UnboundedSender<Result<nymvpn_controller::proto::DaemonEvent, Status>>;
pub type DaemonEventsListenerReceiver =
UnboundedReceiverStream<Result<nymvpn_controller::proto::DaemonEvent, Status>>;
#[tonic::async_trait]
impl ControllerService for ControllerServiceImpl {
type WatchEventsStream = DaemonEventsListenerReceiver;
/// Locations served
async fn get_locations(&self, _: Request<()>) -> ServiceResult<Locations> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::ListLocations(tx))?;
self.wait_for_result(rx)
.await?
.map(|locations| Response::new(locations.into()))
.map_err(map_daemon_error)
}
async fn recent_locations(&self, _: Request<()>) -> ServiceResult<Locations> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::RecentLocations(tx))?;
self.wait_for_result(rx)
.await?
.map(|locations| Response::new(locations.into()))
.map_err(map_daemon_error)
}
/// Account
async fn is_authenticated(&self, _req: Request<()>) -> ServiceResult<bool> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::IsAuthenticated(tx))?;
self.wait_for_result(rx)
.await?
.map(Response::new)
.map_err(map_daemon_error)
}
async fn account_sign_in(&self, req: Request<SignInRequest>) -> ServiceResult<()> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::AccountSignIn(tx, req.into_inner().into()))?;
self.wait_for_result(rx)
.await?
.map(Response::new)
.map_err(map_daemon_error)
}
async fn account_sign_out(&self, _: Request<()>) -> ServiceResult<()> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::AccountSignOut(tx))?;
self.wait_for_result(rx)
.await?
.map(Response::new)
.map_err(map_daemon_error)
}
async fn get_account_info(&self, _: Request<()>) -> ServiceResult<AccountInfo> {
todo!()
}
/// Control VPN
async fn connect_vpn(
&self,
req: Request<nymvpn_controller::proto::Location>,
) -> ServiceResult<VpnStatus> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::Connect(tx, req.into_inner().into()))?;
self.wait_for_result(rx)
.await?
.map(nymvpn_controller::proto::VpnStatus::from)
.map(Response::new)
.map_err(map_daemon_error)
}
async fn disconnect_vpn(&self, _: Request<()>) -> ServiceResult<VpnStatus> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::Disconnect(tx))?;
self.wait_for_result(rx)
.await?
.map(nymvpn_controller::proto::VpnStatus::from)
.map(Response::new)
.map_err(map_daemon_error)
}
async fn get_vpn_status(&self, _: Request<()>) -> ServiceResult<VpnStatus> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::GetVpnStatus(tx))?;
self.wait_for_result(rx)
.await?
.map(nymvpn_controller::proto::VpnStatus::from)
.map(Response::new)
.map_err(map_daemon_error)
}
/// Notifications
async fn get_notifications(&self, _: Request<()>) -> ServiceResult<Notifications> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::GetNotifications(tx))?;
self.wait_for_result(rx)
.await?
.map(nymvpn_controller::proto::Notifications::from)
.map(Response::new)
.map_err(map_daemon_error)
}
async fn ack_notification(&self, id: Request<String>) -> ServiceResult<()> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::AckNotification(tx, id.into_inner()))?;
self.wait_for_result(rx)
.await?
.map(Response::new)
.map_err(map_daemon_error)
}
// Versions and Updates
async fn latest_app_version(&self, _: Request<()>) -> ServiceResult<String> {
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::LatestAppVersion(tx))?;
self.wait_for_result(rx)
.await?
.map(Response::new)
.map_err(map_daemon_error)
}
/// Event stream
async fn watch_events(&self, _: Request<()>) -> ServiceResult<Self::WatchEventsStream> {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
let mut subscribers = self.events_subscribers.write().await;
subscribers.push(tx);
Ok(Response::new(UnboundedReceiverStream::new(rx)))
}
}
impl ControllerServiceImpl {
fn send_command_to_daemon(&self, command: DaemonCommand) -> Result<(), Status> {
self.daemon_command_sender
.send(command)
.map_err(|_| Status::internal("the daemon channel receiver has been dropped"))
}
async fn wait_for_result<T>(&self, rx: tokio::sync::oneshot::Receiver<T>) -> Result<T, Status> {
rx.await.map_err(|_| Status::internal("sender was dropped"))
}
}
fn map_db_error(_db_err: DbErr) -> Status {
Status::internal("daemon is unable to manage its database")
}
pub const SERVER_UNAVAILABLE_PLEASE_TRY_AGAIN_LATER: &str =
"server is unavailable, please try again later";
pub const VPN_SESSION_SERVICE_UNAVAILABLE: &str =
"daemon is partially up: vpn session service unavailable";
fn map_daemon_error(error: DaemonError) -> Status {
tracing::error!("{:?}", error);
match error {
DaemonError::DaemonUnavailable => Status::internal("daemon is unavailable"),
DaemonError::AnotherVpnSessionInProgress(location) => Status::failed_precondition(format!(
"cannot start a new vpn session when another is in progress (to city {})",
location.city
)),
DaemonError::InvalidOpVpnSessionInProgress(message) => Status::failed_precondition(message),
DaemonError::DbErr(db_err) => map_db_error(db_err),
DaemonError::DeviceError(device_error) => match device_error {
crate::device::DeviceError::DeviceServiceUnavailable => {
Status::internal("daemon is partially up: device service unavailable")
}
crate::device::DeviceError::Server(status) => status,
crate::device::DeviceError::Connection(_) => {
Status::unavailable(SERVER_UNAVAILABLE_PLEASE_TRY_AGAIN_LATER)
}
crate::device::DeviceError::DbErr(db_err) => map_db_error(db_err),
crate::device::DeviceError::InitError(_) => {
Status::internal("failed to initialize device")
}
},
DaemonError::VpnSessionError(vpn_session_error) => match vpn_session_error {
crate::vpn_session::handler::VpnSessionError::VpnSessionServiceDown => {
Status::internal(VPN_SESSION_SERVICE_UNAVAILABLE)
}
crate::vpn_session::handler::VpnSessionError::Connection(_) => {
Status::unavailable(SERVER_UNAVAILABLE_PLEASE_TRY_AGAIN_LATER)
}
crate::vpn_session::handler::VpnSessionError::Server(status) => status,
},
}
}
+732
View File
@@ -0,0 +1,732 @@
use std::fmt::Display;
use talpid_core::tunnel_state_machine::{TunnelCommand, TunnelStateMachineHandle};
use talpid_types::tunnel::TunnelStateTransition;
use tokio::sync::oneshot;
use nymvpn_controller::auth::Auth;
use nymvpn_migration::DbErr;
use nymvpn_types::{
location::Location,
notification::Notification,
nymvpn_server::{ClientConnected, EndSession, NewSession, UserCredentials, VpnSessionStatus},
vpn_session::VpnStatus,
};
use crate::{
controller::ControllerServerAndEventBroadcaster,
device::{handler::DeviceHandler, storage::DeviceStorage, DeviceError},
location_storage::LocationStorage,
shutdown::Shutdown,
state::DaemonState,
vpn_session::{
handler::{VpnSessionError, VpnSessionHandler},
storage::{SessionInfo, VpnSessionStorage},
},
ResponseTx,
};
pub struct Daemon {
// in memory current state of the server + client side state
state: DaemonState,
controller_server_and_event_broadcaster: ControllerServerAndEventBroadcaster,
daemon_command_sender: DaemonCommandSender,
daemon_receiver: DaemonReceiver,
device_handler: DeviceHandler,
vpn_session_storage: VpnSessionStorage,
device_storage: DeviceStorage,
vpn_session_handler: VpnSessionHandler,
tunnel_state_machine_handle: TunnelStateMachineHandle,
location_storage: LocationStorage,
shutdown: Option<Shutdown>,
}
#[derive(Debug, thiserror::Error)]
pub enum DaemonError {
#[error("daemon is offline")]
DaemonUnavailable,
#[error("another vpn session in progress: {0}")]
AnotherVpnSessionInProgress(Location),
#[error("invalid op when vpn session in progress: {0}")]
InvalidOpVpnSessionInProgress(String),
#[error("")]
DbErr(#[from] DbErr),
#[error("device error: {0}")]
DeviceError(#[from] DeviceError),
#[error("vpn session error: {0}")]
VpnSessionError(#[from] VpnSessionError),
}
#[derive(Debug)]
pub enum DaemonCommand {
IsAuthenticated(ResponseTx<bool, DaemonError>),
AccountSignIn(ResponseTx<(), DaemonError>, UserCredentials),
AccountSignOut(ResponseTx<(), DaemonError>),
ListLocations(ResponseTx<Vec<Location>, DaemonError>),
RecentLocations(ResponseTx<Vec<Location>, DaemonError>),
Connect(ResponseTx<VpnStatus, DaemonError>, Location),
Disconnect(ResponseTx<VpnStatus, DaemonError>),
GetVpnStatus(ResponseTx<VpnStatus, DaemonError>),
GetNotifications(ResponseTx<Vec<Notification>, DaemonError>),
AckNotification(ResponseTx<(), DaemonError>, String),
LatestAppVersion(ResponseTx<String, DaemonError>),
}
pub type DaemonReceiver = tokio::sync::mpsc::UnboundedReceiver<DaemonEvent>;
pub type DaemonSender = tokio::sync::mpsc::UnboundedSender<DaemonEvent>;
pub struct DaemonCommandChannel {
sender: DaemonCommandSender,
receiver: DaemonReceiver,
}
impl DaemonCommandChannel {
pub fn new() -> Self {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
Self {
sender: DaemonCommandSender(tx),
receiver: rx,
}
}
pub fn sender(&self) -> DaemonCommandSender {
self.sender.clone()
}
pub fn destructure(self) -> (DaemonCommandSender, DaemonReceiver) {
(self.sender, self.receiver)
}
}
#[derive(Clone)]
pub struct DaemonCommandSender(DaemonSender);
#[derive(Clone)]
pub struct DaemonEventSender(DaemonSender);
impl DaemonCommandSender {
pub fn send(&self, command: DaemonCommand) -> Result<(), DaemonError> {
self.0
.send(DaemonEvent::Command(command))
.map_err(|_| DaemonError::DaemonUnavailable)
}
}
impl DaemonEventSender {
pub fn send(&self, event: DaemonEvent) -> Result<(), DaemonError> {
self.0
.send(event)
.map_err(|_| DaemonError::DaemonUnavailable)
}
}
impl<E> talpid_core::mpsc::Sender<E> for DaemonEventSender
where
DaemonEvent: From<E>,
{
fn send(&self, event: E) -> Result<(), talpid_core::mpsc::Error> {
self.0
.send(DaemonEvent::from(event))
.map_err(|_| talpid_core::mpsc::Error::ChannelClosed)
}
}
impl From<DaemonCommandSender> for DaemonEventSender {
fn from(dcs: DaemonCommandSender) -> Self {
Self(dcs.0)
}
}
/// All possible events that can happen during the lifetime of a Daemon
#[derive(Debug)]
pub enum DaemonEvent {
/// Command for the Daemon
Command(DaemonCommand),
/// Initiated by signals like ctrl-c, SIGINT or SIGTERM
Shutdown,
/// Vpn Session Status received from Server
VpnSessionStatus(VpnSessionStatus),
/// Tunnel has changed state.
TunnelStateTransition(TunnelStateTransition),
}
impl From<TunnelStateTransition> for DaemonEvent {
fn from(tst: TunnelStateTransition) -> Self {
DaemonEvent::TunnelStateTransition(tst)
}
}
impl Display for DaemonEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let event = match self {
DaemonEvent::Command(command) => match command {
DaemonCommand::IsAuthenticated(_) => "IsAuthenticated".into(),
DaemonCommand::AccountSignIn(_, user_creds) => {
format!("AccountSignIn: {}", user_creds.email)
}
DaemonCommand::AccountSignOut(_) => "AccountSignOut".into(),
DaemonCommand::ListLocations(_) => "ListLocations".into(),
DaemonCommand::RecentLocations(_) => "RecentLocations".into(),
DaemonCommand::Connect(_, location) => format!("Connect: {}", location.code),
DaemonCommand::Disconnect(_) => "Disconnect".into(),
DaemonCommand::GetVpnStatus(_) => "GetVpnStatus".into(),
DaemonCommand::GetNotifications(_) => "GetNotifications".into(),
DaemonCommand::AckNotification(_, id) => format!("AckNotification: {id}"),
DaemonCommand::LatestAppVersion(_) => "LatestAppVersion".into(),
},
DaemonEvent::Shutdown => "Shutdown".into(),
DaemonEvent::VpnSessionStatus(status) => format!("VpnSessionStatus: {status}"),
DaemonEvent::TunnelStateTransition(transition) => match transition {
TunnelStateTransition::Disconnected => "TunnelStateTransition: Disconnected".into(),
TunnelStateTransition::Connecting(endpoint) => {
format!("TunnelStateTransition: {endpoint}")
}
TunnelStateTransition::Connected(endpoint) => {
format!("TunnelStateTransition: {endpoint}")
}
TunnelStateTransition::Disconnecting(action) => {
format!("TunnelStateTransition: {action:?}")
}
TunnelStateTransition::Error(e) => format!("TunnelStateTransition: {e:?}"),
},
};
write!(f, "{event}")
}
}
#[async_trait::async_trait]
pub trait EventListener {
async fn send_vpn_status(&self, status: VpnStatus);
async fn send_notification(&self, notification: Notification);
//todo: add other events
}
impl Daemon {
pub fn new(
dc: DaemonCommandChannel,
device_handler: DeviceHandler,
vpn_session_storage: VpnSessionStorage,
device_storage: DeviceStorage,
vpn_session_handler: VpnSessionHandler,
controller_server_and_event_broadcaster: ControllerServerAndEventBroadcaster,
tunnel_state_machine_handle: TunnelStateMachineHandle,
location_storage: LocationStorage,
shutdown: Option<Shutdown>,
) -> Self {
let (daemon_command_sender, daemon_receiver) = dc.destructure();
Daemon {
state: DaemonState::new(),
daemon_command_sender,
daemon_receiver,
device_handler,
device_storage,
vpn_session_storage,
vpn_session_handler,
controller_server_and_event_broadcaster,
tunnel_state_machine_handle,
location_storage,
shutdown,
}
}
fn register_shutdown(&mut self) {
let mut shutdown = self.shutdown.take().unwrap();
let sender = DaemonEventSender::from(self.daemon_command_sender.clone());
tokio::spawn(async move {
shutdown.recv().await;
if let Err(e) = sender.send(DaemonEvent::Shutdown) {
//todo: Display trait
tracing::error!("failed to send shutdown event to Daemon: {e:#?}");
}
});
}
pub async fn run(mut self) {
self.register_shutdown();
while let Some(event) = self.daemon_receiver.recv().await {
if let DaemonEvent::Shutdown = event {
break;
}
self.handle_event(event).await;
}
self.handle_shutdown().await;
}
async fn handle_shutdown(mut self) {
tracing::info!("handling shutdown ...");
// if any, disconnect and end existing session
if let Err(err) = self.on_disconnect_inner("daemon shutdown".into()).await {
tracing::error!("when ending session during shutdown: {err}");
};
// wait for tunnel state machine to stop
self.tunnel_state_machine_handle.try_join().await;
if let Err(err) = self.vpn_session_handler.shutdown().await {
tracing::error!("error when vpn session handler was shutting down: {err}");
};
let ControllerServerAndEventBroadcaster {
events_subscribers,
controller_server_handle,
} = self.controller_server_and_event_broadcaster;
let mut guard = events_subscribers.write().await;
guard.clear();
drop(guard);
drop(events_subscribers);
let _ = tokio::join!(controller_server_handle);
// device handler last as controller depends on it for auth
if let Err(err) = self.device_handler.shutdown().await {
tracing::error!("error when device handler was shutting down: {err}");
};
}
async fn handle_event(&mut self, event: DaemonEvent) {
tracing::debug!("daemon event: {event}");
match event {
DaemonEvent::Command(command) => self.handle_command(command).await,
DaemonEvent::VpnSessionStatus(vpn_session_status) => {
self.handle_vpn_session_status(vpn_session_status).await
}
DaemonEvent::TunnelStateTransition(transition) => {
self.handle_tunnel_state_transition(transition).await
}
DaemonEvent::Shutdown => {}
}
}
async fn handle_tunnel_state_transition(&mut self, transition: TunnelStateTransition) {
tracing::info!("tunnel transition: {transition:?}");
let processed = self
.vpn_session_storage
.tunnel_state_transition(transition.clone(), self.state.vpn_status())
.await
.expect("failed to process tunnel state transition");
if let Some(reason) = processed.end_session {
tracing::info!("ending session after tunnel transition {reason}");
if let Err(err) = self.end_session(reason).await {
tracing::error!(
"failed to end session on state transition: {transition:?}: {err:?}"
);
};
}
if let Some(session_info) = processed.client_connected {
tracing::info!("client connected {}", session_info.request_id);
self.client_connected(session_info).await;
}
if let Some(tunnel_command) = processed.tunnel_command {
tracing::info!("sending tunnel command after tunnel state transition");
self.send_tunnel_command(tunnel_command);
}
if let Some(notification) = processed.notification {
tracing::info!("sending notification after tunnel state transition {notification:?}");
self.add_notification(notification).await;
}
// update vpn status and send device event
self.set_vpn_status(processed.vpn_status).await;
}
async fn handle_vpn_session_status(&mut self, vpn_session_status: VpnSessionStatus) {
// Update DB and get next set of actions
let processed = self
.vpn_session_storage
.updated_server_status(vpn_session_status.clone())
.await
.map_err(|e| {
tracing::error!(
"failed to process updated vpn session status {vpn_session_status}: {e}"
)
})
.expect("unrecoverable db error in handle_vpn_session_status");
if let Some(notification) = processed.notification {
self.add_notification(notification).await;
}
if let Some(new_vpn_status) = processed.vpn_status {
// Update in memory status and notify clients of new status
self.set_vpn_status(new_vpn_status.clone()).await;
// Start next state machine if this is ServerReady
self.update_tunnel_on_new_status(new_vpn_status).await;
}
}
async fn update_tunnel_on_new_status(&self, new_vpn_status: VpnStatus) {
if let VpnStatus::ServerReady(_) = new_vpn_status {
self.send_tunnel_command(TunnelCommand::Connect);
}
}
async fn handle_command(&mut self, command: DaemonCommand) {
match command {
DaemonCommand::IsAuthenticated(tx) => self.is_authenticated(tx).await,
DaemonCommand::AccountSignIn(tx, auth_input) => {
self.on_account_sign_in(tx, auth_input).await
}
DaemonCommand::AccountSignOut(tx) => self.on_account_sign_out(tx).await,
DaemonCommand::ListLocations(tx) => self.on_list_locations(tx).await,
DaemonCommand::RecentLocations(tx) => self.on_recent_locations(tx).await,
DaemonCommand::Connect(tx, location) => self.on_connect(tx, location).await,
DaemonCommand::Disconnect(tx) => self.on_disconnect(tx).await,
DaemonCommand::GetVpnStatus(tx) => self.on_get_vpn_status(tx).await,
DaemonCommand::GetNotifications(tx) => self.on_get_notifications(tx).await,
DaemonCommand::AckNotification(tx, id) => self.on_ack_notification(tx, id).await,
DaemonCommand::LatestAppVersion(tx) => self.on_latest_app_version(tx).await,
}
}
async fn on_latest_app_version(&self, tx: ResponseTx<String, DaemonError>) {
let device_handler = self.device_handler.clone();
tokio::spawn(async move {
Self::oneshot_send(
tx,
device_handler
.latest_app_version()
.await
.map_err(DaemonError::DeviceError),
"on_latest_app_version",
);
});
}
async fn on_get_notifications(&self, tx: ResponseTx<Vec<Notification>, DaemonError>) {
let notifications = self.state.notifications();
tokio::spawn(async move {
Self::oneshot_send(tx, Ok(notifications), "on_get_notifications");
});
}
async fn on_ack_notification(&mut self, tx: ResponseTx<(), DaemonError>, id: String) {
self.state.remove_notification(id);
tokio::spawn(async move {
Self::oneshot_send(tx, Ok(()), "on_ack_notification");
});
}
async fn add_notification(&mut self, notification: Notification) {
// save in current state
self.state.add_notification(notification.clone());
// notify event listeners
self.controller_server_and_event_broadcaster
.send_notification(notification)
.await;
}
async fn set_vpn_status(&mut self, vpn_status: nymvpn_types::vpn_session::VpnStatus) {
// save current state
self.state.set_vpn_status(vpn_status.clone());
// and notify event listeners,
{
// todo: make broadcaster clone-able and send event on spawned task
self.controller_server_and_event_broadcaster
.send_vpn_status(vpn_status.into())
.await;
}
}
async fn on_connect_inner(&mut self, location: Location) -> Result<VpnStatus, DaemonError> {
if let Some(location) = self.state.vpn_session_in_progress() {
tracing::warn!("another vpn session in progress: {location}");
return Err(DaemonError::AnotherVpnSessionInProgress(location));
}
let request_id = self
.vpn_session_storage
.new_session(location.clone())
.await?;
let device_unique_id = self.device_storage.get_device_unique_id().await?;
let new_session = NewSession {
request_id,
device_unique_id,
location_code: location.code.clone(),
};
match self
.vpn_session_handler
.new_session(new_session.clone())
.await
{
Ok(accepted) => {
// update local record
self.vpn_session_storage
.update_on_accepted(accepted)
.await?;
// update state
self.state.accepted(location.clone());
Ok(VpnStatus::Accepted(location))
}
Err(err) => {
tracing::error!("cannot connect: {err}");
// remove local record
self.vpn_session_storage
.delete(new_session.request_id)
.await?;
// add notification about it
let notification = self
.state
.add_notification_for_failed_new_session(request_id, location, err);
self.add_notification(notification).await;
Ok(VpnStatus::Disconnected)
}
}
}
async fn on_connect(
&mut self,
tx: ResponseTx<VpnStatus, DaemonError>,
location: nymvpn_types::location::Location,
) {
tracing::info!("Connection requested to {location}");
let location_storage = self.location_storage.clone();
let location_to_add = location.clone();
tokio::spawn(async move {
if let Err(e) = location_storage.add_recent(location_to_add).await {
tracing::error!("failed to save recent location: {e}");
}
});
Self::oneshot_send(tx, self.on_connect_inner(location).await, "on_connect");
}
async fn tunnel_command_on_disconnect(&self) {
let current_state = self.state.vpn_status();
match &current_state {
VpnStatus::ServerReady(_) | VpnStatus::Connecting(_) | VpnStatus::Connected(_, _) => {
self.send_tunnel_command(TunnelCommand::Disconnect)
}
VpnStatus::Accepted(_)
| VpnStatus::ServerCreated(_)
| VpnStatus::ServerRunning(_)
| VpnStatus::Disconnecting(_)
| VpnStatus::Disconnected => {
tracing::info!("Not sending tunnel command to disconnect. State: {current_state}");
}
}
}
async fn end_session(&mut self, end_reason: String) -> Result<(), DaemonError> {
// Get session info and mark for deletion in DB
let session_info = self.vpn_session_storage.end_session().await?;
match session_info {
Some(session_info) => {
let device_unique_id = self.device_storage.get_device_unique_id().await?;
let end_session = EndSession {
request_id: session_info.request_id,
device_unique_id,
vpn_session_uuid: session_info.vpn_session_id,
reason: end_reason,
};
// make api call to server to end session,
// ignore errors as reclaimer should eventually cleanup
match self
.vpn_session_handler
.end_session(end_session.clone())
.await
{
Ok(ended) => {
tracing::info!("vpn session successfully ended on server: {ended}");
// on success delete record from DB
self.vpn_session_storage
.delete(session_info.request_id)
.await?;
}
Err(err) => {
tracing::error!("couldn't end session on server {end_session}: {err}");
}
};
}
None => {
tracing::warn!("No existing vpn session found in DB in end_session");
}
}
Ok(())
}
async fn client_connected(&self, session_info: SessionInfo) {
let device_storage = self.device_storage.clone();
let vpn_session_handler = self.vpn_session_handler.clone();
tokio::spawn(async move {
async fn call_client_connected(
session_info: SessionInfo,
device_storage: DeviceStorage,
vpn_session_handler: VpnSessionHandler,
) -> Result<(), DaemonError> {
let device_unique_id = device_storage.get_device_unique_id().await?;
let client_connected = ClientConnected {
request_id: session_info.request_id,
device_unique_id,
vpn_session_uuid: session_info.vpn_session_id,
};
let _ = vpn_session_handler
.client_connected(client_connected.clone())
.await;
Ok(())
}
if let Err(e) =
call_client_connected(session_info.clone(), device_storage, vpn_session_handler)
.await
{
tracing::error!(
"couldn't make client connected call on server: {session_info}: {e}"
);
}
Ok::<(), DaemonError>(())
});
}
async fn on_disconnect_inner(&mut self, end_reason: String) -> Result<VpnStatus, DaemonError> {
self.tunnel_command_on_disconnect().await;
self.end_session(end_reason).await?;
let status = self.state.update_state_on_disconnect();
self.controller_server_and_event_broadcaster
.send_vpn_status(status.clone())
.await;
Ok(status)
}
async fn on_disconnect(&mut self, tx: ResponseTx<VpnStatus, DaemonError>) {
tracing::info!("Disconnect requested");
Self::oneshot_send(
tx,
self.on_disconnect_inner("client requested".into()).await,
"on_disconnect",
);
}
async fn on_get_vpn_status(&self, tx: ResponseTx<VpnStatus, DaemonError>) {
let vpn_status = self.state.vpn_status();
tokio::spawn(async move {
Self::oneshot_send(tx, Ok(vpn_status), "on_get_vpn_status_response")
});
}
async fn is_authenticated(&self, tx: ResponseTx<bool, DaemonError>) {
let device_handler = self.device_handler.clone();
tokio::spawn(async move {
Self::oneshot_send(
tx,
Ok(device_handler.is_authenticated().await),
"is_authenticated_response",
)
});
}
async fn on_account_sign_in(
&self,
tx: ResponseTx<(), DaemonError>,
user_creds: UserCredentials,
) {
// if vpn session in progress do not sign in
if let Some(location) = self.state.vpn_session_in_progress() {
tracing::warn!("sign in attempt when vpn session in progress: {location}");
Self::oneshot_send(
tx,
Err(DaemonError::InvalidOpVpnSessionInProgress(format!(
"cannot sign in when a vpn session is in progress (to city {})",
location.city
))),
"on_account_sign_in error",
);
} else {
let device_handler = self.device_handler.clone();
tokio::spawn(async move {
Self::oneshot_send(
tx,
device_handler
.sign_in(user_creds)
.await
.map_err(DaemonError::DeviceError),
"on_account_login response",
)
});
}
}
async fn on_account_sign_out(&self, tx: ResponseTx<(), DaemonError>) {
// if vpn session in progress do not sign out
if let Some(location) = self.state.vpn_session_in_progress() {
tracing::warn!("sign out attempt when vpn session in progress: {location}");
Self::oneshot_send(
tx,
Err(DaemonError::InvalidOpVpnSessionInProgress(format!(
"cannot sign out when a vpn session is in progress (to city {})",
location.city
))),
"on_account_sign_out error",
);
} else {
let device_handler = self.device_handler.clone();
tokio::spawn(async move {
Self::oneshot_send(
tx,
device_handler
.sign_out()
.await
.map_err(DaemonError::DeviceError),
"on_account_sign_out response",
)
});
}
}
async fn on_list_locations(&self, tx: ResponseTx<Vec<Location>, DaemonError>) {
let vpn_session_handler = self.vpn_session_handler.clone();
tokio::spawn(async move {
Self::oneshot_send(
tx,
vpn_session_handler
.list_locations()
.await
.map_err(DaemonError::VpnSessionError),
"on_list_locations response",
)
});
}
async fn on_recent_locations(&self, tx: ResponseTx<Vec<Location>, DaemonError>) {
let location_storage = self.location_storage.clone();
tokio::spawn(async move {
Self::oneshot_send(
tx,
location_storage.recent().await.map_err(DaemonError::DbErr),
"on_recent_locations response",
)
});
}
fn send_tunnel_command(&self, command: TunnelCommand) {
self.tunnel_state_machine_handle
.command_tx()
.unbounded_send(command)
.expect("Tunnel state machine has stopped");
}
fn oneshot_send<T>(tx: oneshot::Sender<T>, t: T, msg: &'static str) {
if tx.send(t).is_err() {
tracing::warn!("Unable to send {} to the daemon command sender", msg);
}
}
}

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