Feature: Nym Node Ansible playbook & docs (#6266)

* + init ansible folder setup

* / restructure project

* / move hostname

* add ufw

* sort out latest binary

* tweak inventory

* add description toml

* add tunnel and quic roles

* add tunnel and quic and nnginx roles

* add postinst and tags

* add landing page

* add description

* quic fully noninteractive

* add bonding playbook

* add serial

* finish bonding

* initialise ansible docs

* initialise orchestration page

* start docs

* create upgrade flow

* fix upgrade flow

* remove redundant

* add more vars

* finish deploy and bond

* ansible guide

* orchestration intro

* syntax fix

* bump stats and fix syntax

* typo

* fix CVE-2025-66478 vulnerability

* update pnpm

* add pnpm-workspace

* add monorepo root packages

* fix inventory/all

* add moniker comment note

* fix inventory/all

---------

Co-authored-by: RadekSabacky <radek@nymtech.net>
This commit is contained in:
import this
2025-12-10 16:17:08 +00:00
committed by GitHub
parent d7a7bbe525
commit 59aeb63272
45 changed files with 1536 additions and 70 deletions
+191
View File
@@ -0,0 +1,191 @@
# config file for ansible -- http://ansible.com/
# ==============================================
# nearly all parameters can be overridden in ansible-playbook
# or with command line flags. ansible will read ANSIBLE_CONFIG,
# ansible.cfg in the current working directory, .ansible.cfg in
# the home directory or /etc/ansible/ansible.cfg, whichever it
# finds first
[defaults]
# some basic default values...
inventory = inventory/all
#library = /usr/share/my_modules/
remote_tmp = $HOME/.ansible/tmp
pattern = *
forks = 5
poll_interval = 15
transport = smart
remote_port = 22
module_lang = C
# plays will gather facts by default, which contain information about
# the remote system.
#
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
gathering = implicit
# additional paths to search for roles in, colon separated
roles_path = ../roles
# uncomment this to disable SSH key host checking
host_key_checking = False
# what flags to pass to sudo
#sudo_flags = -H
# SSH timeout
timeout = 100
# default user to use for playbooks if user is not specified
# (/usr/bin/ansible will use current user as default)
#remote_user = root
# logging is off by default unless this path is defined
# if so defined, consider logrotate
#log_path = /var/log/ansible.log
# default module name for /usr/bin/ansible
#module_name = command
# use this shell for commands executed under sudo
# you may need to change this to bin/bash in rare instances
# if sudo is constrained
#executable = /bin/sh
# if inventory variables overlap, does the higher precedence one win
# or are hash values merged together? The default is 'replace' but
# this can also be set to 'merge'.
#hash_behaviour = replace
# list any Jinja2 extensions to enable here:
#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n
# if set, always use this private key file for authentication, same as
# if passing --private -key to ansible or ansible-playbook
#private_key_file = /path/to/file
# format of string {{ ansible_managed }} available within Jinja2
# templates indicates to users editing templates files will be replaced.
# replacing {file}, {host} and {uid} and strftime codes with proper values.
ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
# by default, ansible-playbook will display "Skipping [host]" if it determines a task
# should not be run on a host. Set this to "False" if you don't want to see these "Skipping"
# messages. NOTE: the task header will still be shown regardless of whether or not the
# task is skipped.
#display_skipped_hosts = True
# by default (as of 1.3), Ansible will raise errors when attempting to dereference
# Jinja2 variables that are not set in templates or action lines. Uncomment this line
# to revert the behavior to pre-1.3.
#error_on_undefined_vars = False
# by default (as of 1.6), Ansible may display warnings based on the configuration of the
# system running ansible itself. This may include warnings about 3rd party packages or
# other conditions that should be resolved if possible.
# to disable these warnings, set the following value to False:
#system_warnings = True
# by default (as of 1.4), Ansible may display deprecation warnings for language
# features that should no longer be used and will be removed in future versions.
# to disable these warnings, set the following value to False:
#deprecation_warnings = True
# (as of 1.8), Ansible can optionally warn when usage of the shell and
# command module appear to be simplified by using a default Ansible module
# instead. These warnings can be silenced by adjusting the following
# setting or adding warn=yes or warn=no to the end of the command line
# parameter string. This will for example suggest using the git module
# instead of shelling out to the git command.
# command_warnings = False
# set plugin path directories here, separate with colons
action_plugins = ../../other/plugins/action
callback_plugins = ../../other/plugins/callback
connection_plugins = ../../other/plugins/connection
lookup_plugins = ../../other/plugins/lookup
vars_plugins = ../../other/plugins/vars
filter_plugins = ../../other/plugins/filter
# by default callbacks are not loaded for /bin/ansible, enable this if you
# want, for example, a notification or logging callback to also apply to
# /bin/ansible runs
#bin_ansible_callbacks = False
# don't like cows? that's unfortunate.
# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1
#nocows = 1
# don't like colors either?
# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1
#nocolor = 1
# the CA certificate path used for validating SSL certs. This path
# should exist on the controlling node, not the target nodes
# common locations:
# RHEL/CentOS: /etc/pki/tls/certs/ca-bundle.crt
# Fedora : /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
# Ubuntu : /usr/share/ca-certificates/cacert.org/cacert.org.crt
#ca_file_path =
# the http user-agent string to use when fetching urls. Some web server
# operators block the default urllib user agent as it is frequently used
# by malicious attacks/scripts, so we set it to something unique to
# avoid issues.
#http_user_agent = ansible-agent
# if set to a persistant type (not 'memory', for example 'redis') fact values
# from previous runs in Ansible will be stored. This may be useful when
# wanting to use, for example, IP information from one group of servers
# without having to talk to them in the same playbook run to get their
# current IP information.
fact_caching = memory
[paramiko_connection]
# uncomment this line to cause the paramiko connection plugin to not record new host
# keys encountered. Increases performance on new host additions. Setting works independently of the
# host key checking setting above.
#record_host_keys=False
# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this
# line to disable this behaviour.
#pty=False
[ssh_connection]
# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it
#ssh_args = -o ControlMaster=auto -o ControlPersist=60s
# The path to use for the ControlPath sockets. This defaults to
# "%(directory)s/ansible-ssh-%%h-%%p-%%r", however on some systems with
# very long hostnames or very long path names (caused by long user names or
# deeply nested home directories) this can exceed the character limit on
# file socket names (108 characters for most platforms). In that case, you
# may wish to shorten the string below.
#
# Example:
# control_path = %(directory)s/%%h-%%r
#control_path = %(directory)s/ansible-ssh-%%h-%%p-%%r
# Enabling pipelining reduces the number of SSH operations required to
# execute a module on the remote server. This can result in a significant
# performance improvement when enabled, however when using "sudo:" you must
# first disable 'requiretty' in /etc/sudoers
#
# By default, this option is disabled to preserve compatibility with
# sudoers configurations that have requiretty (the default on many distros).
#
#pipelining = False
# if True, make ansible use scp if the connection type is ssh
# (default is sftp)
scp_if_ssh = True
+8
View File
@@ -0,0 +1,8 @@
---
- name: Nym node bonding / post-installation
hosts: all # or a specific host/group
gather_facts: false
serial: 1
roles:
- role: postinstall
+10
View File
@@ -0,0 +1,10 @@
---
- name: "Deploy Nym node"
hosts: all # or a specific host/group
become: true
roles:
- base
- nym
- nginx
- tunnel # comment out for mixnode
- quic # comment out for mixnode or non-wireguard gateway
@@ -0,0 +1,44 @@
---
ansible_ssh_private_key_file: ~/.ssh/<SSH_KEY>
# nym_version: "v2025.21-mozzarella"
#
# NOTE:
# if you want to pin Nym to a specific version instead of using the
# latest release from GitHub in /tasks/main.yml then
# uncomment the line above and set the tag
cli_url: "https://github.com/nymtech/nym/releases/download/nym-binaries-{{ nym_version }}/nym-cli"
tunnel_manager_url: "https://github.com/nymtech/nym/raw/refs/heads/develop/scripts/nym-node-setup/network-tunnel-manager.sh"
quic_bridge_deployment_url: "https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/quic_bridge_deployment.sh"
# NOTE: These values will be used globally unless overwritten per node in inventory/all
ansible_user: root # used for ssh, like `ssh root@nym-exit.ch-1.mynodes.net`
email: "<EMAIL>" # used in certbot, description.toml and landing page
website: "<WEBSITE>" # it is used in the description.toml
description: "<NODE_PUBLIC_DESCRIPTION>" # or define per node in inventory/all
# NOTE: Set these vars if you want them globally for all nodes
# Per node changes in inventory/all will overwrite these global ones:
hostname: "" # this is a fallback, keep it and setup hostname per node in inventory/all
# moniker: "<MONIKER>" # if not setup here not in inventory/all it get's derived from the hostname
# mode: <MODE> # entry-gateway/exit-gateway/mixnode
# wireguard_enabled: <WIREGUARD_ENABLED> # true/false
# NOTE: Possible vars to incule on landing page, etc.
# operator_name: "<OPERATOR_NAME>"
packages:
- tmux
- speedtest-cli
- nano
- htop
- git
- zip
- nala
- curl
- neovim
- ca-certificates
- jq
- wget
- ufw
+34
View File
@@ -0,0 +1,34 @@
[nym_nodes]
# READ CONFIGURATION GUIDE:
# https://nym.com//docs/operators/orchestration/ansible#configuration
# VARIABLES INFO
# required vars to set values per node:
# `ansible_host`, `hostname`, `location`
# global vars can be set in the group_vars/all.yml, for example:
# `email`, `ansible_user`, `moniker`, `description`, `mode`, `wireguard_enabled`
# othersise they must be set per node!
############
# TEMPLATE #
############
# node1 ansible_host=<YOUR_SERVER_IP> ansible_user=<USER> hostname=<HOSTNAME> location=<LOCATION> email=<EMAIL> mode=<MODE> wireguard_enabled=<true/false> moniker=<MONIKER> description=<DESCRIPTION>
# remove all comments and exchange the <VARIABLES> with your real values for each node
# without <> brackets
# PRIORITY ORDER
# anything setup globaly can be overwritten in this file per node
# if provided here, it takes priority over the global setting
# EXAMPLES
# exit + wireguard gateway:
# node2 ansible_host=11.12.13.14 hostname=nym-exit.ch-1.mydomain.net mode=exit-gateway location=CH wireguard_enabled=true
# entry gateway, no wireguard:
# node3 ansible_host=12.13.14.15 hostname=nym-entry.ch-2.mydomain.net mode=entry-gateway location=CH wireguard_enabled=false
# NOTE:
# all examples above don't have defined user, email nor description as we use the definition from group_vars/main.yml without an attempt of overwriting it
# all examples above don't have moniker defined as there is a function in /templates/description.toml.j2 deriving it from the hostname
+9
View File
@@ -0,0 +1,9 @@
---
- name: "Upgrade Nym node"
hosts: all # or a specific host/group or limit via -l on CLI (ansible-playbook playbooks/upgrade.yml -l mynode1)
become: true
serial: 1
roles:
- base
- upgrade
@@ -0,0 +1,22 @@
- name: Set hostname
hostname:
name: "{{ hostname }}"
when: hostname is defined and hostname | length > 0
- name: Install aptitude
apt:
name: aptitude
update_cache: yes
state: present
force_apt_get: yes
- name: Update packages
apt:
update_cache: yes
upgrade: yes
- name: Install essential packages
package:
name: "{{ packages }}"
state: latest
update_cache: yes
@@ -0,0 +1,61 @@
- name: Install nginx and certbot
apt:
name:
- nginx
- certbot
- python3-certbot-nginx
state: present
- name: Create web root directory
file:
path: "/var/www/{{ hostname }}"
state: directory
mode: "0755"
- name: Create landing page template
tags: landing
template:
src: landing.html.j2
dest: "/var/www/{{ hostname }}/index.html"
- name: Remove default nginx site
file:
path: /etc/nginx/sites-enabled/default
state: absent
- name: Add bare-bones nginx template
template:
src: nginx-site.conf.j2
dest: "/etc/nginx/sites-available/{{ hostname }}"
- name: Enable nginx config
file:
src: "/etc/nginx/sites-available/{{ hostname }}"
dest: "/etc/nginx/sites-enabled/{{ hostname }}"
state: link
- name: Validate nginx configuration
command: nginx -t
changed_when: false
- name: Obtain SSL certificate
command:
cmd: "certbot --nginx --non-interactive --agree-tos --redirect -m {{ email }} -d {{ hostname }}"
- name: Add wss config from nginx template
template:
src: wss-config.conf.j2
dest: "/etc/nginx/sites-available/nym-wss-config"
- name: Enable WSS config
file:
src: "/etc/nginx/sites-available/nym-wss-config"
dest: "/etc/nginx/sites-enabled/nym-wss-config"
state: link
- name: Validate nginx config after wss
command: nginx -t
changed_when: false
- name: Restart nginx to apply changes
service: name=nginx state=restarted enabled=yes
@@ -0,0 +1,218 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title>This is a NYM Exit Gateway</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="">
<style>
:root {
/* font + theme tokens */
font-family: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace;
--background-color: #242B2D;
--text-color: #FFFFFF;
--link-color: #07ff94;
--title-color: #07ff94;
}
html {
background: var(--background-color);
}
body {
margin-left: auto;
margin-right: auto;
padding-left: 5vw;
padding-right: 5vw;
max-width: 1000px;
color: var(--text-color); /* default text color */
}
h1 {
font-size: 55px;
text-align: center;
color: var(--title-color);
}
p {
color: var(--text-color);
}
p, a {
font-size: 20px;
}
a {
color: var(--link-color);
text-decoration: none;
}
a:hover {
filter: brightness(.8);
text-decoration: underline;
}
.links {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
.links > a {
margin: 10px;
white-space: nowrap;
}
</style>
</head>
<body>
<main>
<h1>This is a NYM Exit Gateway</h1>
<p>
You are most likely accessing this website because you've had some issue with
the traffic coming from this IP. This router is part of the <a
href="https://nym.com/">NYM project</a>, which is
dedicated to <a href="https://nym.com/about/mission">create</a> outstanding
privacy software that is legally compliant without sacrificing integrity or
having any backdoors.
This router IP should be generating no other traffic, unless it has been
compromised.
</p>
<p><strong>
If you are a representative of a company who feels that this router is being
used to violate the DMCA, please be aware that this machine does not host or
contain any illegal content. Also be aware that network infrastructure
maintainers are not liable for the type of content that passes over their
equipment, in accordance with <a
href="https://www.law.cornell.edu/uscode/text/17/512">DMCA
"safe harbor" provisions</a>. In other words, you will have just as much luck
sending a takedown notice to the Internet backbone providers.
</strong></p>
<p>
Nym Network is operated by a decentralised community of node operators
and stakers. Nym Network is trustless, meaning that no parts of the system
nor its operators have access to information that might compromise the privacy
of users. Nym software enacts a strict principle of data minimisation and has
no back doors. The Nym mixnet works by encrypting packets in several layers
and relaying those through a multi-layered network called a mixnet, eventually
letting the traffic exit the Nym mixnet through an exit gateway like this one.
This design makes it impossible for a service to know which user is connecting to it,
since it can only see the IP-address of the Nym exit gateway:
</p>
<p style="text-align:center;margin:40px 0">
<svg xmlns="http://www.w3.org/2000/svg" width="500" viewBox="0 0 490.28 293.73" style="width:100%;max-width:600px" role="img" aria-label="Diagram of how a user connects through the Nym network">
<desc>Illustration showing how a user might connect to a service through the Nym Network. The user first sends their data through three daisy-chained encrypted Nym nodes that exist on three different continents. Then the last Nym node in the chain connects to the target service over the normal internet.</desc>
<defs>
<style>
.t {
fill: var(--text-color);
stroke: var(--text-color);
}
</style>
</defs>
<!-- (SVG content left unchanged) -->
<path fill="#6fc8b7" d="M257.89 69.4c-6.61-6.36-10.62-7.73-18.36-8.62-7.97-1.83-20.06-7.99-24.17-.67-3.29 5.85-18.2 12.3-16.87 2.08.92-7.03 11.06-13.28 17-17.37 8.69-5.99 24.97-2.87 26.1-10.28 1.04-6.86-8.33-13.22-8.55-2.3-.38 12.84-19.62 2.24-8.73-6.2 8.92-6.9 16.05-9.02 25.61-6.15 12.37 4.83 25.58-2.05 33.73-.71 12.37-2.01 24.69-5.25 37.39-3.96 13 .43 24.08-.14 37.06.63 9.8 1.58 16.5 2.87 26.37 3.6 6.6.48 17.68-.82 24.3 1.9 8.3 4.24.44 10.94-6.89 11.8-8.79 1.05-23.59-1.19-26.6 1.86-5.8 7.41 10.75 5.68 11.27 14.54.57 9.45-5.42 9.38-8.72 16-2.7 4.2.3 13.93-1.18 18.45-1.85 5.64-19.64 4.47-14.7 14.4 4.16 8.34 1.17 19.14-10.33 12.02-5.88-3.65-9.85-22.04-15.66-21.9-11.06.27-11.37 13.18-12.7 17.52-1.3 4.27-3.79 2.33-6-.63-3.54-4.76-7.75-14.22-12.01-17.32-6.12-4.46-10.75-1.17-15.55 2.83-5.63 4.69-8.78 7.82-7.46 16.5.78 9.1-12.9 15.84-14.98 24.09-2.61 10.32-2.57 22.12-8.81 31.47-4 5.98-14.03 20.12-21.27 14.97-7.5-5.34-7.22-14.6-9.56-23.08-2.5-9.02.6-17.35-2.57-26.2-2.45-6.82-6.23-14.54-13.01-13.24-6.5.92-15.08 1.38-19.23-2.97-5.65-5.93-6-10.1-6.61-18.56 1.65-6.94 5.79-12.64 10.38-18.63 3.4-4.42 17.45-10.39 25.26-7.83 10.35 3.38 17.43 10.5 28.95 8.57 3.12-.53 9.14-4.65 7.1-6.62zm-145.6 37.27c-4.96-1.27-11.57 1.13-11.8 6.94-1.48 5.59-4.82 10.62-5.8 16.32.56 6.42 4.34 12.02 8.18 16.97 3.72 3.85 8.58 7.37 9.3 13.1 1.24 5.88 1.6 11.92 2.28 17.87.34 9.37.95 19.67 7.29 27.16 4.26 3.83 8.4-2.15 6.52-6.3-.54-4.54-.6-9.11 1.01-13.27 4.2-6.7 7.32-10.57 12.44-16.64 5.6-7.16 12.74-11.75 14-20.9.56-4.26 5.72-13.86 1.7-16.72-3.14-2.3-15.83-4-18.86-6.49-2.36-1.71-3.86-9.2-9.86-12.07-4.91-3.1-10.28-6.73-16.4-5.97zm11.16-49.42c6.13-2.93 10.58-4.77 14.61-10.25 3.5-4.28 2.46-12.62-2.59-15.45-7.27-3.22-13.08 5.78-18.81 8.71-5.96 4.2-12.07-5.48-6.44-10.6 5.53-4.13.38-9.2-5.66-8.48-6.12.8-12.48-1.45-18.6-1.73-5.3-.7-10.13-1-15.45-1.37-5.37-.05-16.51-2.23-25.13.87-5.42 1.79-12.5 5.3-16.73 9.06-4.85 4.2.2 7.56 5.54 7.45 5.3-.22 16.8-5.36 20.16.98 3.68 8.13-5.82 18.29-5.2 26.69.1 6.2 3.37 11 4.74 16.98 1.62 5.94 6.17 10.45 10 15.14 4.7 5.06 13.06 6.3 19.53 8.23 7.46.14 3.34-9.23 3.01-14.11 1.77-7.15 8.49-7.82 12.68-13.5 7.14-7.72 16.41-13.4 24.34-18.62zM190.88 3.1c-4.69 0-13.33.04-18.17-.34-7.65.12-13.1-.62-19.48-1.09-3.67.39-9.09 3.34-5.28 7.04 3.8.94 7.32 4.92 7.1 9.31 1.32 4.68 1.2 11.96 6.53 13.88 4.76-.2 7.12-7.6 11.93-8.25 6.85-2.05 12.5-4.58 17.87-9.09 2.48-2.76 7.94-6.38 5.26-10.33-1.55-1.31-2.18-.64-5.76-1.13zm178.81 157.37c-2.66 10.08-5.88 24.97 9.4 15.43 7.97-5.72 12.58-2.02 17.47 1.15.5.43 2.65 9.2 7.19 8.53 5.43-2.1 11.55-5.1 14.96-11.2 2.6-4.62 3.6-12.39 2.76-13.22-3.18-3.43-6.24-11.03-7.7-15.1-.76-2.14-2.24-2.6-2.74-.4-2.82 12.85-6.04 1.22-10.12-.05-8.2-1.67-29.62 7.17-31.22 14.86z"/>
<g fill="none">
<path stroke="#cf63a6" stroke-linecap="round" stroke-width="2.76" d="M135.2 140.58c61.4-3.82 115.95-118.83 151.45-103.33"/>
<path stroke="#cf63a6" stroke-linecap="round" stroke-width="2.76" d="M74.43 46.66c38.15 8.21 64.05 42.26 60.78 93.92M286.65 37.25c-9.6 39.44-3.57 57.12-35.64 91.98"/>
<path stroke="#e4c101" stroke-dasharray="9.06,2.265" stroke-width="2.27" d="M397.92 162.52c-31.38 1.26-90.89-53.54-148.3-36.17"/>
<path stroke="#cf63a6" stroke-linecap="round" stroke-width="2.77" d="M17.6 245.88c14.35 0 14.4.05 28-.03"/>
<path stroke="#e3bf01" stroke-dasharray="9.06,2.265" stroke-width="2.27" d="M46.26 274.14c-17.52-.12-16.68.08-30.34.07"/>
</g>
<g transform="translate(120.8 -35.81)">
<circle cx="509.78" cy="68.74" r="18.12" fill="#240a3b" transform="translate(-93.3 38.03) scale(.50637)"/>
<circle cx="440.95" cy="251.87" r="18.12" fill="#240a3b" transform="translate(-93.3 38.03) scale(.50637)"/>
<circle cx="212.62" cy="272.19" r="18.12" fill="#240a3b" transform="translate(-93.3 38.03) scale(.50637)"/>
<circle cx="92.12" cy="87.56" r="18.12" fill="#240a3b" transform="translate(-93.3 38.03) scale(.50637)"/>
<circle cx="730.88" cy="315.83" r="18.12" fill="#67727b" transform="translate(-93.3 38.03) scale(.50637)"/>
<circle cx="-102.85" cy="282.18" r="9.18" fill="#240a3b"/>
<circle cx="-102.85" cy="309.94" r="9.18" fill="#67727b"/>
</g>
<g class="t">
<text xml:space="preserve" x="-24.76" y="10.37" stroke-width=".26" font-size="16.93" font-weight="700" style="line-height:1.25" transform="translate(27.79 2.5)" word-spacing="0"><tspan x="-24.76" y="10.37">The user</tspan></text>
<text xml:space="preserve" x="150.63" y="196.62" stroke-width=".26" font-size="16.93" font-weight="700" style="line-height:1.25" transform="translate(27.79 2.5)" word-spacing="0"><tspan x="150.63" y="196.62">This server</tspan></text>
<text xml:space="preserve" x="346.39" y="202.63" stroke-width=".26" font-size="16.93" font-weight="700" style="line-height:1.25" transform="translate(27.79 2.5)" word-spacing="0"><tspan x="346.39" y="202.63">Your service</tspan></text>
<text xml:space="preserve" x="34.52" y="249.07" stroke-width=".26" font-size="16.93" font-weight="700" style="line-height:1.25" transform="translate(27.79 2.5)" word-spacing="0"><tspan x="34.52" y="249.07">Nym network link</tspan></text>
<text xml:space="preserve" x="34.13" y="276.05" stroke-width=".26" font-size="16.93" font-weight="700" style="line-height:1.25" transform="translate(27.79 2.5)" word-spacing="0"><tspan x="34.13" y="276.05">Unencrypted link</tspan></text>
<path fill="none" stroke-linecap="round" stroke-width="1.67" d="M222.6 184.1c-2.6-15.27 8.95-23.6 18.43-38.86m186.75 45.61c-.68-10.17-9.4-17.68-18.08-23.49"/>
<path fill="none" stroke-linecap="round" stroke-width="1.67" d="M240.99 153.41c.35-3.41 1.19-6.17.04-8.17m-7.15 5.48c1.83-2.8 4.58-4.45 7.15-5.48"/>
<path fill="none" stroke-linecap="round" stroke-width="1.67" d="M412.43 173.21c-2.2-3.15-2.54-3.85-2.73-5.85m0 0c2.46-.65 3.85.01 6.67 1.24M61.62 40.8C48.89 36.98 36.45 27.54 36.9 18.96M61.62 40.8c.05-2.58-3.58-4.8-5.25-5.26m-2.65 6.04c1.8.54 6.8 1.31 7.9-.78"/>
<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.44" d="M1.22 229.4h247.74v63.1H1.22z"/>
</g>
</svg>
</p>
<p><a href="https://nym.com/about/mixnet">Read more about how Nym works.</a></p>
<p>
Nym relies on a growing ecosystem of users, developers and researcher partners
aligned with the mission to make sure Nym software is running, remains usable
and solves real problems. While Nym is not designed for malicious computer
users, it is true that they can use the network for malicious ends. This
is largely because criminals and hackers have significantly better access to
privacy and anonymity than do the regular users whom they prey upon. Criminals
can and do build, sell, and trade far larger and more powerful networks than
Nym on a daily basis. Thus, in the mind of this operator, the social need for
easily accessible censorship-resistant private, anonymous communication trumps
the risk of unskilled bad actors, who are almost always more easily uncovered
by traditional police work than by extensive monitoring and surveillance anyway.
</p>
<p>
In terms of applicable law, the best way to understand Nym is to consider it a
network of routers operating as common carriers, much like the Internet
backbone. However, unlike the Internet backbone routers, Nym mixnodes do not
contain identifiable routing information about the source of a packet and do
mix the user internet traffic with that of other users, making communications
private and protecting not just the user content but the metadata
(user's IP address, who the user talks to, when, where, from what device and
more) and no single Nym node can determine both the origin and destination
of a given transmission.
</p>
<p>
As such, there is nothing the operator of this Exit Gateway can do to help you
track the connection further. This Exit Gateway maintains no logs of any of the
Nym Network, so there is little that can be done to trace either legitimate or
illegitimate traffic and most importantly the operator cannot tell apart one from
the other because of the cryptography design making such selection impossible
for the operator. Attempts to seize this router will accomplish nothing.
</p>
<p>
To decentralise and enable privacy for a broad range of services, this
Exit Gateway adopts an <a href="https://nymtech.net/.wellknown/network-requester/exit-policy.txt">Exit Policy</a>
serving as a safeguard.
</p>
<p>
That being said, if you still have a complaint about the router, you may email the
<a href="mailto:{{ email }}">maintainer</a>. If complaints are related to a particular service that is being abused,
the maintainer will submit that to the NYM Operators Community in order to add it to the Exit Policy cited above.
The community governance can only blacklist entire IP:port destinations across the entire network.
</p>
<p>
You also have the option of blocking this IP address and others on the Nym network if you so desire.
The Nym project provides a <a href="https://nym.com/explorer">
web service</a> to fetch a list of all IP addresses of Nym Gateway Exit nodes that allow exiting to a
specified IP:port combination. Please be considerate when using these options.
</p>
<p style="text-align:center">
<img
class="logo"
src="https://raw.githubusercontent.com/nymtech/websites/main/www/nym.com/public/images/Nym_meta_Image.png"
alt=""
style="max-width:320px;width:100%;height:auto"
onerror="this.onerror=null;this.src='/images/nym_logo.png';"
/>
</p>
</main>
</body>
</html>
@@ -0,0 +1,13 @@
server {
listen 80;
listen [::]:80;
server_name {{ hostname }};
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
@@ -0,0 +1,35 @@
server {
listen 9001 ssl http2;
listen [::]:9001 ssl http2;
server_name {{ hostname }};
ssl_certificate /etc/letsencrypt/live/{{ hostname }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ hostname }}/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location /favicon.ico {
return 204;
access_log off;
log_not_found off;
}
location / {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD' always;
add_header 'Access-Control-Allow-Headers' '*' always;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://localhost:9000;
proxy_intercept_errors on;
}
}
@@ -0,0 +1,40 @@
---
# Where binaries live
nym_install_dir: /root/nym-binaries
# nym-node run arguments (defaults, can be overridden per host/group)
http_bind_address: "0.0.0.0:8080" # maps to --http-bind-address
mixnet_bind_address: "0.0.0.0:1789" # maps to --mixnet-bind-address
# WireGuard boolean
wireguard_enabled: "{{ wireguard_enabled | default(false) | bool }}"
# Landing page base dir, hostname is appended in the task
landing_page_assets_base_dir: "/var/www"
# Flag toggles
# accept_operator_terms: true # controls --accept-operator-terms-and-conditions
nym_write_flag: true # controls -w
nym_init_only_flag: true # controls --init-only
wss_port: 9001 # controlls --announce-wss-port
# Optional: extra flags if you want to append more later
nym_extra_flags: ""
# CLI URL (nym_version can be set elsewhere / via GitHub API)
nym_cli_url: "https://github.com/nymtech/nym/releases/download/{{ nym_version }}/nym-cli"
# UFW
nym_ufw_enable: true
nym_ufw_rules:
- { port: 22, proto: tcp }
- { port: 80, proto: tcp }
- { port: 443, proto: tcp }
- { port: 1789, proto: tcp }
- { port: 1790, proto: tcp }
- { port: 8080, proto: tcp }
- { port: 9000, proto: tcp }
- { port: 9001, proto: tcp }
- { port: 51822, proto: udp }
@@ -0,0 +1 @@
#!/bin/bash
@@ -0,0 +1,3 @@
- name: Reload systemd
systemd:
daemon_reload: yes
@@ -0,0 +1,38 @@
---
# Useful when the host is behind a NAT
- name: Fetch the public IP address
command: "curl -4 canhazip.com"
register: ipv4
changed_when: false
failed_when: false
- name: Set public IP address
set_fact:
public_ip: "{{ ipv4.stdout | default(ansible_default_ipv4.address) }}"
- name: Initialize nym node
# Delete the part from --hostname onward if you run mode=mixnode only
command:
cmd: >
{{ nym_install_dir }}/nym-node run
--mode {{ mode }}
--public-ips {{ public_ip }}
--http-bind-address {{ http_bind_address }}
--mixnet-bind-address {{ mixnet_bind_address }}
--location {{ location }}
{% if accept_operator_terms %}--accept-operator-terms-and-conditions{% endif %}
{{ nym_extra_flags }}
--hostname {{ hostname }}
--wireguard-enabled {{ wireguard_enabled }}
--landing-page-assets-path {{ landing_page_assets_base_dir }}/{{ hostname }}/
{% if nym_write_flag %}-w{% endif %}
{% if nym_init_only_flag %}--init-only{% endif %}
--announce-wss-port {{ wss_port }}
- name: Update nym description
template:
src: description.toml.j2
dest: /root/.nym/nym-nodes/default-nym-node/data/description.toml
@@ -0,0 +1,25 @@
- name: Configure UFW rules
ufw:
rule: allow
port: "{{ item.port }}"
proto: "{{ item.proto }}"
comment: "{{ item.comment | default(omit) }}"
loop: "{{ nym_ufw_rules }}"
loop_control:
label: "{{ item.port }}/{{ item.proto }}"
when:
- nym_ufw_enable
- item.when | default(true)
- name: Allow bandwidth/topup rule inside WG tunnel
command: >
ufw allow in on nymwg to any port 51830 proto tcp comment 'bandwidth queries/topup'
when:
- nym_ufw_enable
- (wireguard_enabled | bool)
- name: Enable UFW
ufw:
state: enabled
when:
nym_ufw_enable
@@ -0,0 +1,34 @@
---
- name: Create nym directory
file:
path: "{{ nym_install_dir }}"
state: directory
mode: "0755"
- name: Get latest Nym release metadata
uri:
url: https://api.github.com/repos/nymtech/nym/releases/latest
return_content: yes
register: latest_release
when: nym_version is not defined or nym_version == 'latest'
- name: Set nym_version from GitHub API
set_fact:
nym_version: "{{ latest_release.json.tag_name }}"
when: nym_version is not defined or nym_version == 'latest'
- name: Set binary URL
set_fact:
binary_url: "https://github.com/nymtech/nym/releases/download/{{ nym_version }}/nym-node"
- name: Download nym-node binary
get_url:
url: "{{ binary_url }}"
dest: "{{ nym_install_dir }}/nym-node"
mode: "0755"
- name: Download nym-cli binary
get_url:
url: "{{ nym_cli_url }}"
dest: "{{ nym_install_dir }}/nym-cli"
mode: "0755"
+12
View File
@@ -0,0 +1,12 @@
---
- name: Install Nym binaries
import_tasks: install.yml
- name: Configure Nym node
import_tasks: config.yml
- name: Configure firewall for Nym
import_tasks: firewall.yml
- name: Configure and start Nym service
import_tasks: service.yml
@@ -0,0 +1,15 @@
---
- name: Template systemd service
tags: systemctl
template:
src: nym-node.service.j2
dest: /etc/systemd/system/nym-node.service
notify: Reload systemd
- name: Enable and start nym service
tags: systemctl
systemd:
name: nym-node
enabled: yes
state: started
daemon_reload: yes
@@ -0,0 +1,20 @@
{# Priority:
1. Use moniker if provided in inventory
2. Else strip "nym-exit." prefix if hostname starts with it
3. Else use hostname unchanged
#}
{% if moniker is defined and moniker | length > 0 %}
{% set moniker_final = moniker %}
{% else %}
{% if hostname is defined and hostname.startswith('nym-exit.') %}
{% set moniker_final = hostname | regex_replace('^nym-exit\\.', '') %}
{% else %}
{% set moniker_final = hostname %}
{% endif %}
{% endif %}
moniker = "{{ moniker_final }}"
website = " {{ website }}"
security_contact = "{{ email }}"
details = "{{ description }}"
@@ -0,0 +1,15 @@
[Unit]
Description=Nym Node
StartLimitInterval=350
StartLimitBurst=10
[Service]
User={{ ansible_user }}
LimitNOFILE=65536
ExecStart=/root/nym-binaries/nym-node run --mode {{ mode }} --accept-operator-terms-and-conditions --wireguard-enabled {{ wireguard_enabled }}
KillSignal=SIGINT
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,49 @@
- name: Show which node is being bonded
tags: bonding
debug:
msg: "Bonding Nym node: {{ hostname }}"
- name: Get bonding details
tags: bonding
command: "/root/nym-binaries/nym-node bonding-information"
register: bondinfo
changed_when: false
- name: Display bonding info
tags: bonding
debug:
msg: "{{ item }}"
loop: "{{ bondinfo.stdout_lines }}"
- name: Prompt operator to generate contract message in wallet
tags: bonding
pause:
prompt: |
** Using the bonding information above:
1. Open your wallet
2. Go to Bonding section
3. Fill Hostname and Identity key from the message printed above
4. Copy the CONTRACT MESSAGE that your wallet displays
5. Paste it below and press Enter
Paste CONTRACT MESSAGE here:
register: contract_msg_input
- name: Sign bonding contract message on the node
tags: bonding
command:
argv:
- /root/nym-binaries/nym-node
- sign
- --contract-msg
- "{{ contract_msg_input.user_input }}"
- --output
- json
register: sign_output
- name: Display full signed message exactly as returned
tags: bonding
debug:
msg: "{{ sign_output.stdout }}"
@@ -0,0 +1,16 @@
- name: Download quic_bridge_deployment.sh
tags: quic bridge deployment
get_url:
url: "{{ quic_bridge_deployment_url }}"
dest: "/root/nym-binaries/quic_bridge_deployment.sh"
mode: "0755"
- name: Configure tunnel manager
tags: quic bridge deployment
become: true
command:
cmd: "/root/nym-binaries/quic_bridge_deployment.sh {{ item }}"
environment:
NONINTERACTIVE: "1"
loop:
- full_bridge_setup
@@ -0,0 +1,14 @@
- name: Download network-tunnel-manager.sh
tags: network tunnel manager
get_url:
url: "{{ tunnel_manager_url }}"
dest: "/root/nym-binaries/network-tunnel-manager.sh"
mode: "0755"
- name: Configure tunnel manager
tags: network tunnel manager
become: true
command:
cmd: "/root/nym-binaries/network-tunnel-manager.sh {{ item }}"
loop:
- complete_networking_configuration
@@ -0,0 +1,10 @@
nym_binary_dir: /root/nym-binaries
nym_binary_path: "{{ nym_binary_dir }}/nym-node"
nym_backup_dir: "{{ nym_binary_dir }}/bak"
nym_backup_path: "{{ nym_backup_dir }}/nym-node"
nym_service_name: nym-node
# nym_version: "v2025.21-mozzarella"
# Optional: set this to pin a specific release tag in (e.g. v2025.21-mozzarella)
# otherwise the GitHub “latest” release is used
@@ -0,0 +1,30 @@
- name: Get latest Nym release metadata
uri:
url: https://api.github.com/repos/nymtech/nym/releases/latest
return_content: yes
register: latest_release
when: nym_version is not defined and not ansible_check_mode
- name: Set nym_version from GitHub API
set_fact:
nym_version: "{{ latest_release.json.tag_name }}"
when: nym_version is not defined and not ansible_check_mode
- name: Show target Nym version tag
debug:
msg: "Target Nym release tag: {{ nym_version | default('latest (check-mode)') }}"
- name: Generate binary_url from version
set_fact:
binary_url: >-
https://github.com/nymtech/nym/releases/download/{{ nym_version }}/nym-node
when: not ansible_check_mode
- name: Download nym-node binary
get_url:
url: "{{ binary_url }}"
dest: "{{ nym_binary_path }}"
mode: "0755"
register: download_result
failed_when: false
when: not ansible_check_mode
@@ -0,0 +1,122 @@
# run --version on the new binary
- name: Check new nym-node version
command:
argv:
- "{{ nym_binary_path }}"
- --version
register: nym_new_version_cmd
failed_when: false
changed_when: false
when: not ansible_check_mode
# show the full stdout so we dont depend on regex parsing at all
# show full upgraded version output, line by line
- name: Show upgraded nym-node version info
debug:
msg: >-
{{
[
"New nym-node --version rc: " ~ (nym_new_version_cmd.rc | default('unset') | string),
"New nym-node --version output:"
]
+ (nym_new_version_cmd.stdout_lines | default([]))
}}
when: not ansible_check_mode
# decide if upgrade is successful
# success means: the binary executed without an error (rc == 0)
- name: Determine if upgrade is successful
set_fact:
upgrade_ok: "{{ (nym_new_version_cmd.rc | default(1)) == 0 }}"
when: not ansible_check_mode
# show the decision for debugging
- name: Debug upgrade_ok decision
debug:
msg:
- "upgrade_ok: {{ upgrade_ok }}"
when: not ansible_check_mode
#########
# success
#########
# show the full version output to the user, line-by-line
- name: Show upgraded nym-node version info
debug:
msg:
- "Upgraded nym-node version output:"
- "{{ nym_new_version_cmd.stdout_lines | default([]) }}"
when: not ansible_check_mode and upgrade_ok | default(false)
# remove backup
- name: Remove backup after successful upgrade
file:
path: "{{ nym_backup_path }}"
state: absent
when:
- not ansible_check_mode
- upgrade_ok | default(false)
- nym_node_bin.stat.exists | default(false)
# restart service
- name: Restart nym-node service after successful upgrade
systemd:
name: "{{ nym_service_name }}"
state: restarted
when: not ansible_check_mode and upgrade_ok | default(false)
# report success
- name: Report successful upgrade
debug:
msg: >-
Upgrade successful. nym-node binary executed correctly and the service has been restarted.
when: not ansible_check_mode and upgrade_ok | default(false)
#########
# failure
#########
- name: Restore previous nym-node binary after failed upgrade
copy:
src: "{{ nym_backup_path }}"
dest: "{{ nym_binary_path }}"
mode: "0755"
remote_src: true
when:
- not ansible_check_mode
- (upgrade_ok | default(false)) == false
- nym_node_bin.stat.exists | default(false)
- name: Remove backup after rollback
file:
path: "{{ nym_backup_path }}"
state: absent
when:
- not ansible_check_mode
- (upgrade_ok | default(false)) == false
- nym_node_bin.stat.exists | default(false)
# always restart the service with the restored binary
- name: Restart nym-node service with previous version after failed upgrade
systemd:
name: "{{ nym_service_name }}"
state: restarted
when:
- not ansible_check_mode
- (upgrade_ok | default(false)) == false
- nym_node_bin.stat.exists | default(false)
- name: Report failed upgrade and rollback
debug:
msg: >-
Upgrade NOT successful. The previous nym-node binary has been restored
and the nym-node service has been restarted with the old version.
when: not ansible_check_mode and (upgrade_ok | default(false)) == false
# optional: hard-fail the play for CI environments
#- name: Fail the play to signal upgrade failure
# fail:
# msg: "nym-node upgrade failed; rolled back to previous binary."
# when: not ansible_check_mode and (upgrade_ok | default(false)) == false
@@ -0,0 +1,8 @@
- name: Prepare for nym-node upgrade (backup, stop service)
include_tasks: prepare.yml
- name: Fetch and install latest nym-node binary
include_tasks: fetch_latest.yml
- name: Verify new nym-node and finalize (restart or rollback)
include_tasks: finalize.yml
@@ -0,0 +1,69 @@
# stop service before touching the binary
- name: Stop nym-node service
systemd:
name: "{{ nym_service_name }}"
state: stopped
when: not ansible_check_mode
# check if the current binary exists
- name: Check existing nym-node binary
stat:
path: "{{ nym_binary_path }}"
register: nym_node_bin
# capture current nym-node version (if present)
- name: Capture current nym-node version (if present)
command:
argv:
- "{{ nym_binary_path }}"
- --version
register: nym_current_version_cmd
failed_when: false
changed_when: false
when:
- nym_node_bin.stat.exists
- not ansible_check_mode
# show full current version output instead of trying to parse it
# show full current version output, line by line
- name: Show current nym-node version info
debug:
msg: >-
{{
[
"Current nym-node --version rc: " ~ (nym_current_version_cmd.rc | default('unset') | string),
"Current nym-node --version output:"
]
+ (nym_current_version_cmd.stdout_lines | default([]))
}}
when:
- nym_node_bin.stat.exists
- not ansible_check_mode
# ensure backup directory exists
- name: Ensure backup directory exists
file:
path: "{{ nym_backup_dir }}"
state: directory
mode: "0755"
when: not ansible_check_mode
# backup existing nym-node binary
- name: Backup existing nym-node binary
copy:
src: "{{ nym_binary_path }}"
dest: "{{ nym_backup_path }}"
remote_src: true
mode: "0755"
when:
- not ansible_check_mode
- nym_node_bin.stat.exists
# remove current nym-node binary
- name: Remove current nym-node binary
file:
path: "{{ nym_binary_path }}"
state: absent
when:
- not ansible_check_mode
- nym_node_bin.stat.exists
@@ -5,7 +5,7 @@
},
"mixmining_reserve": {
"denom": "unym",
"amount": "178754510529387"
"amount": "176683247613141"
},
"vesting_tokens": {
"denom": "unym",
@@ -13,6 +13,6 @@
},
"circulating_supply": {
"denom": "unym",
"amount": "821245489470613"
"amount": "823316752386859"
}
}
@@ -1 +1 @@
821_245_489
823_316_752
@@ -1 +1 @@
60_303_169
60_455_259
@@ -1 +1 @@
60_303_168
60_455_258
@@ -1,7 +1,7 @@
| **Item** | **Description** | **Amount in NYM** |
|:-------------------|:------------------------------------------------------|--------------------:|
| Total Supply | Maximum amount of NYM token in existence | 1_000_000_000 |
| Mixmining Reserve | Tokens releasing for operators rewards | 178_754_510 |
| Mixmining Reserve | Tokens releasing for operators rewards | 176_683_247 |
| Vesting Tokens | Tokens locked outside of cicrulation for future claim | 0 |
| Circulating Supply | Amount of unlocked tokens | 821_245_489 |
| Stake Saturation | Optimal size of node self-bond + delegation | 251_263 |
| Circulating Supply | Amount of unlocked tokens | 823_316_752 |
| Stake Saturation | Optimal size of node self-bond + delegation | 251_896 |
@@ -1,10 +1,10 @@
{
"interval": {
"reward_pool": "178754510529387.687865580967403579",
"staking_supply": "60303168385204.800235781003503301",
"reward_pool": "176683247613141.489518623753339272",
"staking_supply": "60455258984180.809034503857018251",
"staking_supply_scale_factor": "0.07342892",
"epoch_reward_budget": "4965403070.260769107377249094",
"stake_saturation_point": "251263201605.02000098242084793",
"epoch_reward_budget": "4907867989.25393026440621537",
"stake_saturation_point": "251896912434.086704310432737576",
"sybil_resistance": "0.3",
"active_set_work_factor": "10",
"interval_pool_emission": "0.02"
@@ -1 +1 @@
Wednesday, November 26th 2025, 12:13:56 UTC
Tuesday, December 9th 2025, 12:54:38 UTC
+1 -1
View File
@@ -45,7 +45,7 @@
"chain-registry": "^1.19.0",
"cosmjs-types": "^0.9.0",
"lucide-react": "^0.438.0",
"next": "^15.2.4",
"next": "15.5.7",
"nextra": "2",
"nextra-theme-docs": "2",
"react": "^18.2.0",
@@ -6,6 +6,7 @@
"sandbox": "Sandbox Testnet",
"binaries": "Binaries",
"nodes": "Nodes & Validators Guides",
"orchestration" : "Orchestration",
"performance-and-testing": "Performance Measurement",
"tools": "Tools",
"troubleshooting": "Troubleshooting",
@@ -0,0 +1,10 @@
# Orchestration of Multiple Nym Nodes
Nym is a distributed network operated by admins all around the world. As the network grows, it attracts experienced operators of various infrastructure and builds an ever evolving community of builders, developers, admins and privacy enthusiasts.
Operators form squads, DAOs and different entities managing multiple nodes per organisation. Orchestration of multiple servers with Nym nodes allows operators to be more efficient in deployment, changes implementation and upgrade flow following our demanding [release cycle](/operators/release-cycle).
The operators who understand well basic `nym-node` [requirements](/operators/nodes#minimum-requirements) and general server administration, can use these two guides to lower cost and energy spent to maintain their nodes.
1. [**Virtualising dedicated server with KVM**](/operators/nodes/preliminary-steps/vps-setup/advanced)
2. [**Orchestration of multiple nodes with Ansible**](/operators/orchestration/ansible)
@@ -0,0 +1,265 @@
import { Callout } from 'nextra/components';
import { Tabs } from 'nextra/components';
import { Steps } from 'nextra/components';
import { RunTabs } from 'components/operators/nodes/node-run-command-tabs';
import { VarInfo } from 'components/variable-info.tsx';
# Orchestrating Nym Nodes with Ansible
<VarInfo />
[Ansible](https://docs.ansible.com/) is an open-source automation engine that can perform IT tasks and remove complexity from workflows. Ansible ensures that your environment is exactly as you describe it. You can automate any command with Ansible to make your system maintenance very efficient. **For `nym-node` operators Ansible is particularly useful as it can scale infinitely the amount of nodes operators can setup, bond, upgrade, maintain and re-configure from their local shell, removing the complexity and required time when managing many nodes one by one.**
<Callout type="warning" emoji="⚠️">
**This setup should be used only by operators who understand `nym-node` administration and [requirements](/operators/nodes#minimum-requirements)**
**Ansible is more suitable for skilled power users managing multiple nodes at the same time!**
</Callout>
If you are not familiar with Ansible, operating Nym nodes may be a good motivation to learn something new and improve your admin skills, it's worth the time.
Start by reading through [Ansible documentation pages](https://docs.ansible.com)
## Installation
### Ansible installation
For anything regarding the installation and management of Ansible itself, the best is to refer to their documentation. On [this page](https://docs.ansible.com/projects/ansible/latest/installation_guide/intro_installation.html#latest-release-via-dnf-or-yum) you can see the installation guide.
If you are confident and want to start right away, install Ansible on your machine using one of these two ways:
1. `apt` repository:
```sh
sudo apt-get update
sudo apt-get install ansible
```
2. `pip` or `pipx` - recommended by Ansible community:
```sh
pip install ansible
# or
pipx install ansible
```
### Nym Node Playbook Installation
Nym Node Ansible playbook template is located in our monorepo [`nymtech/nym/ansible/nym-node`](https://github.com/nymtech/nym/tree/develop/ansible/nym-node)
<Steps>
###### 1. Get `nym/ansible/nym-node` playbook:
The easiest way is to use `git` to `clone` or `pull` the repository:
```sh
git clone https://github.com/nymtech/nym.git
# or navigate where you already have the repo and run
git checkout develop
git pull origin develop
```
###### 2. Save the template to your location:
You may want to create a directory outside of the repository and move the template there so it can be modified without risking that your configuration will be accidentally shared when working with the repository in the future.
- Navigate to any location and create a directory for your Ansible `nym-node` playbook:
```sh
cd <PATH>
mkdir `ansible`
cd ansible
```
- Copy the template to the newly created location:
```sh
cp -r <PATH>/nym/ansible/nym-node ./
```
</ Steps>
Now you have the template of Ansible playbook for `nym-node` remote administration. To make it work, there are a few variables requiring your attention.
## Configuration
After [getting the ansible Nym node playbpook](#ansible-installation) to your location, it's time to configure it for your own needs.
> Mind that *idempotency* is an essential character when dealing with orchestration. A playbook, even when run many times should ensure that state of your targeted system will not change from what you intended. Therefore, it is important to make sure that all tasks in your playbook do not change the system in any way if the change you required has already been applied.
<Callout type="warning" emoji="⚠️">
Before starting Ansible, ensure that your `A` and `AAAA` records are pointed to your server IPs and propagated. Good test is to be able to ping them or use them for ssh into the server.
</ Callout>
**Open your local copy of the playbook in your favourite text editor and begin with these steps:**
<Steps>
###### 1. Configure global variables:
- Open `playbooks/group_vars/all.yml`
- Setup any variables which you want to have propagated on all your nodes globally
- Note that in the next step we will be setting up a node inventory, where each of the variable can be configured per node, taking priority over the global ones.
- Setup a correct path for your SSH kety to `ansible_ssh_private_key_file:`
- Use these variables or comment them out with `#`:
- `ansible_user`
- `email`
- `website`
- `description`
- Keep `hostname=""` as a fallback for nodes without a hostname
###### 2. Create node inventory:
- Open `playbooks/inventory/all`
- Make an entry for each of your node:
```sh
node1 ansible_host=<YOUR_SERVER_IP> ansible_user=<USER> hostname=<HOSTNAME> location=<LOCATION> email=<EMAIL> mode=<MODE> wireguard_enabled=<true/false> moniker=<MONIKER> description=<DESCRIPTION>
```
- These are mandatory values specific for each node - must be defined in the inventory:
- `ansible_host`: IPv4 host address
- `hostname`: node domain, otherwise fallbacks to `""` for nodes without domain
- `location`: node server location
- These are mandatory values which can be setup per node or in `group_vars/all` globally:
- `ansible_user`
- `email`
- `website`
- `moniker`
- `description`
- `mode`
- `wireguard_enabled`
###### 3. Test your setup
Run this command to check if everything is configured correctly in your inventory:
```sh
cd playbooks
ansible-inventory --graph
```
###### 4. Configure `nym-node run` command arguments
Open `roles/nym/defaults/main.yml` and have a look on the variables used:
- If you agree with [Terms and conditions](/operators/nodes/nym-node/setup#terms--conditions) uncomment the line: `accept_operator_terms: true` without which your node can never take part in Nym Network.
- The rest is up to your configuration but generally these flags workflows
These variables are read by the main task for `nym-node` installation: `roles/nym/tasks/config.yaml`
- Open that yaml and have a look on the flags
- In case of not needing some of the, delete them (ie when running `--mode mixnode` you can delete everything from `--hostname` to `--announce-wss-port`)
###### 5. Configure `deploy.yml` playbook
Open `playbooks/deploy.yml` and comment out `tunnel` and `quic` roles in case of running your playbook for nodes in a mode `mixnode`.
Save all the files and test with:
```sh
cd playbooks
ansible-inventory --graph
```
Right now you should be ready to go.
</Steps>
## Flow & Usage
This chapter describes fundamental commands for using Ansible playbooks in relation to orchestrating multiple servers running a `nym-node`. For a full understanding of Ansible usage, read [Ansible documentation pages](https://docs.ansible.com).
### Logic
The main logic of the playbook flow when running with a basic command and playbook like this:
```sh
ansible-playbook <PLAYBOOK>.yml
```
<Steps>
###### 1. Read inventory
Ansible parses `inventory/all` and performs the playbook on all entries in it, unless specified otherwise
###### 2. Read global vars
Ansible parses `group_vars/all.yml` and asigns global variables to all inventory entries, unless they were defined in the inventory.
**Variables defined in the inventory per entry take highest priority!**
###### 3. Follow roles in the playbook
Ansible reads the roles defined in `<PLAYBOOK>.yml` passed with the command and executes the tasks defined under each role
</ Steps>
### Usage
The simplest way is to run `ansible-playbook` binary with a provided playbook as a command. That will do the defined roles on all entries in the inventory. In Nym we currently have these playbooks:
<Steps>
###### 1. Deploy
A playbook to deploy server and `nym-node` from scratch, configuring networking, routing, firewall, systemd, bridges, reverse proxy, exit policy and all required tasks.
This playbook will run roles on all the inventory entries in parallel by default.
```sh
cd playbooks
ansible-playbook deploy.yml
```
###### 2. Bond
A playbook to interactively register your node to Nym network by bonding it to Nyx blockchain account.
This playbook is intercative as it prompts user for data from Nym wallet to sign a message. It will run roles on one inventory entry at a time by default.
```sh
cd playbooks
ansible-playbook bond.yml
```
###### 3. Upgrade
A playbook to upgrade `nym-node` binary to the *Latest* by default. Operators can hard code a specific binary version in `roles/upgrade/defaults/main.yml` by un-commenting the `nym-version` line and providing their desired version.
This playbook will run roles on all the inventory entries in parallel by default.
```sh
cd playbooks
ansible-playbook upgrade.yml
```
</ Steps>
### Useful Commands
[Ansible](https://docs.ansible.com) has many smart ways to manage your playbooks, roles or inventory entries.
**Here are some useful tips:**
<Steps>
###### One node at a time
To test new configuration, it's advised to try it on one server at first. Of course you can comment out any entries in the inventory, but there are easier ways to do it.
- Provide flag `-l` followed by inventory entry and Ansible will change state only of that entry:
- Some possibilities are (in example we use upgrade.yml, you can use any playbook):
```sh
# point to one entry
ansible-playbook upgrade.yml -l node1
# point to multiple entries
ansible-playbook upgrade.yml -l "node1,node2"
# use regex
ansible-playbook upgrade.yml -l "*exit*"
```
###### Role limit
Sometimes you may want to run just one role at a time, for that use `-q`, for example:
```sh
# in case of wanting to run only quic deployment role
ansible-playbook deploy.yml -t quic
# in case of running the same on only one node
ansible-playbook deploy.yml -l node2 -t quic
```
###### nocows
Yes, by default there is a cow printed under each task, you can turn it off by opening `playbooks/ansible.cfg` and un-commenting the `nocows` line:
```cfg
nocows = 1
```
</ Steps>
+60 -51
View File
@@ -99,14 +99,14 @@ importers:
specifier: ^0.438.0
version: 0.438.0(react@18.3.1)
next:
specifier: ^15.2.4
version: 15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
specifier: 15.5.7
version: 15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nextra:
specifier: '2'
version: 2.13.4(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 2.13.4(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nextra-theme-docs:
specifier: '2'
version: 2.13.4(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@2.13.4(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 2.13.4(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@2.13.4(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react:
specifier: ^18.2.0
version: 18.3.1
@@ -1009,32 +1009,32 @@ packages:
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
'@next/env@15.5.0':
resolution: {integrity: sha512-sDaprBAfzCQiOgo2pO+LhnV0Wt2wBgartjrr+dpcTORYVnnXD0gwhHhiiyIih9hQbq+JnbqH4odgcFWhqCGidw==}
'@next/env@15.5.7':
resolution: {integrity: sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==}
'@next/eslint-plugin-next@13.4.13':
resolution: {integrity: sha512-RpZeXlPxQ9FLeYN84XHDqRN20XxmVNclYCraLYdifRsmibtcWUWdwE/ANp2C8kgesFRsvwfsw6eOkYNl9sLJ3A==}
'@next/swc-darwin-arm64@15.5.0':
resolution: {integrity: sha512-v7Jj9iqC6enxIRBIScD/o0lH7QKvSxq2LM8UTyqJi+S2w2QzhMYjven4vgu/RzgsdtdbpkyCxBTzHl/gN5rTRg==}
'@next/swc-darwin-arm64@15.5.7':
resolution: {integrity: sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-x64@15.5.0':
resolution: {integrity: sha512-s2Nk6ec+pmYmAb/utawuURy7uvyYKDk+TRE5aqLRsdnj3AhwC9IKUBmhfnLmY/+P+DnwqpeXEFIKe9tlG0p6CA==}
'@next/swc-darwin-x64@15.5.7':
resolution: {integrity: sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-linux-arm64-gnu@15.5.0':
resolution: {integrity: sha512-mGlPJMZReU4yP5fSHjOxiTYvZmwPSWn/eF/dcg21pwfmiUCKS1amFvf1F1RkLHPIMPfocxLViNWFvkvDB14Isg==}
'@next/swc-linux-arm64-gnu@15.5.7':
resolution: {integrity: sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@15.5.0':
resolution: {integrity: sha512-biWqIOE17OW/6S34t1X8K/3vb1+svp5ji5QQT/IKR+VfM3B7GvlCwmz5XtlEan2ukOUf9tj2vJJBffaGH4fGRw==}
'@next/swc-linux-arm64-musl@15.5.7':
resolution: {integrity: sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -1045,20 +1045,26 @@ packages:
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@15.5.0':
resolution: {integrity: sha512-+t3+7GoU9IYmk+N+FHKBNFdahaReoAktdOpXHFIPOU1ixxtdge26NgQEEkJkCw2dHT9UwwK5zw4mAsURw4E8jA==}
'@next/swc-linux-x64-gnu@15.5.7':
resolution: {integrity: sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-win32-arm64-msvc@15.5.0':
resolution: {integrity: sha512-d8MrXKh0A+c9DLiy1BUFwtg3Hu90Lucj3k6iKTUdPOv42Ve2UiIG8HYi3UAb8kFVluXxEfdpCoPPCSODk5fDcw==}
'@next/swc-linux-x64-musl@15.5.7':
resolution: {integrity: sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-win32-arm64-msvc@15.5.7':
resolution: {integrity: sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-x64-msvc@15.5.0':
resolution: {integrity: sha512-Fe1tGHxOWEyQjmygWkkXSwhFcTJuimrNu52JEuwItrKJVV4iRjbWp9I7zZjwqtiNnQmxoEvoisn8wueFLrNpvQ==}
'@next/swc-win32-x64-msvc@15.5.7':
resolution: {integrity: sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -5379,8 +5385,8 @@ packages:
react: '*'
react-dom: '*'
next@15.5.0:
resolution: {integrity: sha512-N1lp9Hatw3a9XLt0307lGB4uTKsXDhyOKQo7uYMzX4i0nF/c27grcGXkLdb7VcT8QPYLBa8ouIyEoUQJ2OyeNQ==}
next@15.5.7:
resolution: {integrity: sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
@@ -8156,33 +8162,36 @@ snapshots:
'@tybys/wasm-util': 0.10.0
optional: true
'@next/env@15.5.0': {}
'@next/env@15.5.7': {}
'@next/eslint-plugin-next@13.4.13':
dependencies:
glob: 7.1.7
'@next/swc-darwin-arm64@15.5.0':
'@next/swc-darwin-arm64@15.5.7':
optional: true
'@next/swc-darwin-x64@15.5.0':
'@next/swc-darwin-x64@15.5.7':
optional: true
'@next/swc-linux-arm64-gnu@15.5.0':
'@next/swc-linux-arm64-gnu@15.5.7':
optional: true
'@next/swc-linux-arm64-musl@15.5.0':
'@next/swc-linux-arm64-musl@15.5.7':
optional: true
'@next/swc-linux-x64-gnu@15.5.0': {}
'@next/swc-linux-x64-musl@15.5.0':
'@next/swc-linux-x64-gnu@15.5.7':
optional: true
'@next/swc-win32-arm64-msvc@15.5.0':
'@next/swc-linux-x64-musl@15.5.7':
optional: true
'@next/swc-win32-x64-msvc@15.5.0':
'@next/swc-win32-arm64-msvc@15.5.7':
optional: true
'@next/swc-win32-x64-msvc@15.5.7':
optional: true
'@nextui-org/accordion@2.2.7(@nextui-org/system@2.4.6(@nextui-org/theme@2.4.5(tailwindcss@4.1.12))(framer-motion@12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@nextui-org/theme@2.4.5(tailwindcss@4.1.12))(framer-motion@12.23.12(@emotion/is-prop-valid@1.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
@@ -14461,21 +14470,21 @@ snapshots:
transitivePeerDependencies:
- supports-color
next-seo@6.8.0(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
next-seo@6.8.0(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
next: 15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next: 15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
next-themes@0.2.1(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
next-themes@0.2.1(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
next: 15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next: 15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@next/env': 15.5.0
'@next/env': 15.5.7
'@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001735
postcss: 8.4.31
@@ -14483,21 +14492,21 @@ snapshots:
react-dom: 18.3.1(react@18.3.1)
styled-jsx: 5.1.6(babel-plugin-macros@3.1.0)(react@18.3.1)
optionalDependencies:
'@next/swc-darwin-arm64': 15.5.0
'@next/swc-darwin-x64': 15.5.0
'@next/swc-linux-arm64-gnu': 15.5.0
'@next/swc-linux-arm64-musl': 15.5.0
'@next/swc-linux-x64-gnu': 15.5.0
'@next/swc-linux-x64-musl': 15.5.0
'@next/swc-win32-arm64-msvc': 15.5.0
'@next/swc-win32-x64-msvc': 15.5.0
'@next/swc-darwin-arm64': 15.5.7
'@next/swc-darwin-x64': 15.5.7
'@next/swc-linux-arm64-gnu': 15.5.7
'@next/swc-linux-arm64-musl': 15.5.7
'@next/swc-linux-x64-gnu': 15.5.7
'@next/swc-linux-x64-musl': 15.5.7
'@next/swc-win32-arm64-msvc': 15.5.7
'@next/swc-win32-x64-msvc': 15.5.7
'@opentelemetry/api': 1.9.0
sharp: 0.34.3
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
nextra-theme-docs@2.13.4(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@2.13.4(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
nextra-theme-docs@2.13.4(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@2.13.4(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@headlessui/react': 1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@popperjs/core': 2.11.8
@@ -14508,16 +14517,16 @@ snapshots:
git-url-parse: 13.1.1
intersection-observer: 0.12.2
match-sorter: 6.3.4
next: 15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-seo: 6.8.0(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes: 0.2.1(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nextra: 2.13.4(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next: 15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-seo: 6.8.0(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes: 0.2.1(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nextra: 2.13.4(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
scroll-into-view-if-needed: 3.1.0
zod: 3.25.76
nextra@2.13.4(next@15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
nextra@2.13.4(next@15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@headlessui/react': 1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@mdx-js/mdx': 2.3.0
@@ -14531,7 +14540,7 @@ snapshots:
gray-matter: 4.0.3
katex: 0.16.22
lodash.get: 4.4.2
next: 15.5.0(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next: 15.5.7(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-mdx-remote: 4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
p-limit: 3.1.0
react: 18.3.1
+9
View File
@@ -0,0 +1,9 @@
packages:
- "."
ignoredBuiltDependencies:
- core-js
- protobufjs
- sharp
- tiny-secp256k1
- unrs-resolver
@@ -356,9 +356,14 @@ run_bridge_cfg_generate() {
NODE_CFG="$HOME_DIR/.nym/nym-nodes/default-nym-node/config/config.toml"
fi
echo -n "Path to your nym-node config.toml [default: $NODE_CFG]: "
read -r input
[[ -n "$input" ]] && NODE_CFG="$input"
if [[ "${NONINTERACTIVE:-0}" == "1" ]]; then
# Noninteractive: just use the detected/default path
info "NONINTERACTIVE=1: using nym-node config at: $NODE_CFG"
else
echo -n "Path to your nym-node config.toml [default: $NODE_CFG]: "
read -r input
[[ -n "$input" ]] && NODE_CFG="$input"
fi
if [[ ! -f "$NODE_CFG" ]]; then
err "nym-node config not found: $NODE_CFG"
@@ -404,6 +409,7 @@ run_bridge_cfg_generate() {
export LAST_BACKUP_FILE="$BACKUP_FILE"
}
# Systemd service
create_bridge_service() {
title "Creating nym-bridge systemd Service"