diff --git a/.github/workflows/cd-dev.yml b/.github/workflows/cd-dev.yml deleted file mode 100644 index 0ca463345f..0000000000 --- a/.github/workflows/cd-dev.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: CD dev-portal - -on: - push: - branches: master - paths: - - 'documentation/dev-portal/**' - -jobs: - build: - runs-on: custom-runner-linux - steps: - - uses: actions/checkout@v3 - - name: Install rsync - run: sudo apt-get install rsync - - uses: rlespinasse/github-slug-action@v3.x - - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: Install Rust stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - name: Install mdbook - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) - - name: Install mdbook plugins - run: | - cargo install --vers "^0.2.0" mdbook-variables && cargo install \ - --vers "^1.8.0" mdbook-admonish && cargo install --vers \ - "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \ - mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck - - name: Clean website - run: cd documentation/dev-portal && mdbook clean - - name: Build website - run: cd documentation/dev-portal && mdbook build - - name: Deploy branch master to dev - continue-on-error: true - uses: easingthemes/ssh-deploy@main - env: - SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} - ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/dev-portal/book/html/" - REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_DEV }} - REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} - TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET_DEVP }}/ - EXCLUDE: "/dist/, /node_modules/" - - name: Deploy branch master to prod - uses: easingthemes/ssh-deploy@main - env: - SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} - ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/dev-portal/book/html/" - REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_PROD }} - REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} - TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET_DEVP }}/ - EXCLUDE: "/dist/, /node_modules/" - - name: Matrix - Node Install - run: npm install - working-directory: .github/workflows/support-files - - name: Matrix - Send Notification - env: - NYM_NOTIFICATION_KIND: cd-dev - NYM_PROJECT_NAME: "Dev portal CD" - NYM_CI_WWW_BASE: "${{ secrets.NYM_CD_WWW_BASE }}" - NYM_CI_WWW_LOCATION: "${{ env.GITHUB_REF_SLUG }}" - GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" - GIT_BRANCH: "${GITHUB_REF##*/}" - MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}" - MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_DEVP }}" - MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}" - MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}" - MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}" - IS_SUCCESS: "${{ job.status == 'success' }}" - uses: docker://keybaseio/client:stable-node - with: - args: .github/workflows/support-files/notifications/entry_point.sh diff --git a/.github/workflows/cd-docs.yml b/.github/workflows/cd-docs.yml index b3f15ab265..a1ba0c104a 100644 --- a/.github/workflows/cd-docs.yml +++ b/.github/workflows/cd-docs.yml @@ -28,39 +28,38 @@ jobs: command: build args: --workspace --release --all - name: Install mdbook - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) + run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook) - name: Install mdbook plugins run: | - cargo install --vers "^0.2.0" mdbook-variables && cargo install \ + cargo install --vers "=0.2.2" mdbook-variables && cargo install \ --vers "^1.8.0" mdbook-admonish && cargo install --vers \ - "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \ - mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck && \ - cargo install --vers "^0.5.0" mdbook-cmdrun - - name: Clean website - run: cd documentation/docs && mdbook clean - - name: Build website - run: cd documentation/docs && mdbook build + "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \ + && cargo install --vers "^0.7.7" mdbook-linkcheck + - name: Build all projects in documentation/ & move to ~/dist/docs/ + run: cd documentation && ./build_all_to_dist.sh + continue-on-error: false - name: Deploy branch master to dev continue-on-error: true uses: easingthemes/ssh-deploy@main env: SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/docs/book/" + SOURCE: "dist/docs/" REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_DEV }} REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/ - EXCLUDE: "/dist/, /node_modules/" + EXCLUDE: "/node_modules/" - name: Deploy branch master to prod + if: github.ref == 'refs/heads/master' uses: easingthemes/ssh-deploy@main env: SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/docs/book/" + SOURCE: "dist/docs/" REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_PROD }} REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/ - EXCLUDE: "/dist/, /node_modules/" + EXCLUDE: "/node_modules/" - name: Matrix - Node Install run: npm install working-directory: .github/workflows/support-files diff --git a/.github/workflows/ci-dev.yml b/.github/workflows/ci-dev.yml deleted file mode 100644 index 80688c2ccc..0000000000 --- a/.github/workflows/ci-dev.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: CI dev-portal - -on: - workflow_dispatch: - push: - branches-ignore: master - paths: - - 'documentation/dev-portal/**' - -jobs: - build: - runs-on: custom-runner-linux - steps: - - uses: actions/checkout@v3 - - name: Install rsync - run: sudo apt-get install rsync - - uses: rlespinasse/github-slug-action@v3.x - - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: Install Rust stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - name: Install mdbook - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) - - name: Install mdbook plugins - run: | - cargo install --vers "^0.2.0" mdbook-variables && cargo install \ - --vers "^1.8.0" mdbook-admonish && cargo install --vers \ - "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \ - && cargo install --vers "^0.7.7" mdbook-linkcheck - - name: Clean website - run: cd documentation/dev-portal && mdbook clean - - name: Build website - run: cd documentation/dev-portal && mdbook build - - name: Deploy branch to CI www - continue-on-error: true - uses: easingthemes/ssh-deploy@main - env: - SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} - ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/dev-portal/book/html/" - REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} - REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} - TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/dev-portal-${{ env.GITHUB_REF_SLUG }} - EXCLUDE: "/dist/, /node_modules/" - - name: Matrix - Node Install - run: npm install - working-directory: .github/workflows/support-files - - name: Matrix - Send Notification - env: - NYM_NOTIFICATION_KIND: ci-dev - NYM_PROJECT_NAME: "Dev portal CI" - NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}" - NYM_CI_WWW_LOCATION: "dev-portal-${{ env.GITHUB_REF_SLUG }}" - GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" - GIT_BRANCH: "${GITHUB_REF##*/}" - MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}" - MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_DEVP }}" - MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}" - MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}" - MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}" - IS_SUCCESS: "${{ job.status == 'success' }}" - uses: docker://keybaseio/client:stable-node - with: - args: .github/workflows/support-files/notifications/entry_point.sh diff --git a/.github/workflows/ci-docs.yml b/.github/workflows/ci-docs.yml index 2523013204..2ce019272a 100644 --- a/.github/workflows/ci-docs.yml +++ b/.github/workflows/ci-docs.yml @@ -1,6 +1,7 @@ name: CI docs on: + workflow_dispatch: push: branches-ignore: master paths: @@ -27,29 +28,27 @@ jobs: command: build args: --workspace --release --all - name: Install mdbook - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) + run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook) - name: Install mdbook plugins run: | - cargo install --vers "^0.2.0" mdbook-variables && cargo install \ + cargo install --vers "=0.2.2" mdbook-variables && cargo install \ --vers "^1.8.0" mdbook-admonish && cargo install --vers \ - "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \ - mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck && \ - cargo install --vers "^0.5.0" mdbook-cmdrun - - name: Clean website - run: cd documentation/docs && mdbook clean - - name: Build website - run: cd documentation/docs && mdbook build + "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \ + && cargo install --vers "^0.7.7" mdbook-linkcheck + - name: Build all projects in documentation/ & move to ~/dist/docs/ + run: cd documentation && ./build_all_to_dist.sh + continue-on-error: false - name: Deploy branch to CI www continue-on-error: true uses: easingthemes/ssh-deploy@main env: SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/docs/book/" + SOURCE: "dist/docs/" REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/docs-${{ env.GITHUB_REF_SLUG }} - EXCLUDE: "/dist/, /node_modules/" + EXCLUDE: "/node_modules/" - name: Matrix - Node Install run: npm install working-directory: .github/workflows/support-files diff --git a/documentation/README.md b/documentation/README.md index 3330b3e969..e074f7cdfe 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -2,5 +2,6 @@ * `docs` contains technical documentation hosted at [https://nymtech.net/docs](https://nymtech.net/docs) * `dev-portal` contains developer documentation hosted at [https://nymtech.net/developers](https://nymtech.net/developers) +* `operators` contains node setup and maintenance guides hosted at [https://nymtech.net/operators](https://nymtech.net/operators) Each directory contains a readme with more information about running and contributing to the projects. Each is built with [`mdbook`](https://rust-lang.github.io/mdBook/index.html) - use `mdbook serve` to build and serve them (defaults to `localhost:3000`). diff --git a/documentation/build_all_to_dist.sh b/documentation/build_all_to_dist.sh new file mode 100755 index 0000000000..721ed07e6a --- /dev/null +++ b/documentation/build_all_to_dist.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# commands assume you run script from `nym/documentation/` + +# array of project dirs +declare -a projects=("docs" "dev-portal" "operators") + +## now loop through the above array +for i in "${projects[@]}" +do + # cd to project dir + cd "./$i" && + # little sanity checks + echo $(pwd) && echo $(mdbook --version) && + # clean old book + echo "cleaning old book" + rm -rf ./book/ + # build book + mdbook build + # check for destination, if ! then mkdir & check again else echo thumbs up + if [ ! -d ../../dist/docs/$i ]; then + echo "dest doesn't exist: creating dir" + mkdir -p ../../dist/docs/$i + fi + if [ -d ../../dist/docs/$i ]; then + echo "cp destination exists, all good" + fi + # clean old dist/$i + rm -rf ../../dist/docs/$i + # move newly rendered book/ to dist + rsync -r ./book/html/ ../../dist/docs/$i + # sanity check + ls -laF ../../dist/docs/ + # cd back to ../documentation/ + cd ../ +done +mv ../dist/docs/dev-portal ../dist/docs/developers diff --git a/documentation/dev-portal/book.toml b/documentation/dev-portal/book.toml index cf434a31f5..d9853f839c 100644 --- a/documentation/dev-portal/book.toml +++ b/documentation/dev-portal/book.toml @@ -17,7 +17,7 @@ src = "src" # - run `mdbook build`: this will rebuild the `./theme` directory # - change value of `turn-off` back to `true` [preprocessor.theme] -pagetoc = true +pagetoc = true # some variables related (defined in theme/css/variables.css) # `content-max-width` + `pagetoc-width` = 95% seems the best pagetoc-width = "13%" @@ -37,7 +37,7 @@ nav-chapters-min-width = "auto" chapter-line-height = "2em" section-line-height = "1.5em" -# # if true, never read and touch the files in theme dir +# if true, never read and touch the files in theme dir turn-off = false [preprocessor.admonish] @@ -49,7 +49,7 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` [preprocessor.variables.variables] minimum_rust_version = "1.66" # vars for links: TODO think on how to streamline updating -platform_release_version = "v1.1.25" +platform_release_version = "v1.1.27" wallet_release_version = "v1.2.7" [preprocessor.last-changed] @@ -77,14 +77,14 @@ curly-quotes = true # mathjax-support = false # useful if we want to pull equations in ? copy-fonts = true no-section-label = false -additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "last-changed.css"] +additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "custom.css"] additional-js = ["theme/pagetoc.js"] git-repository-url = "https://github.com/nymtech/nym" git-repository-icon = "fa-github" # edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" site-url = "/developers/" # cname = "nymtech.net" -# input-404 = "not-found.md" # todo make custom 404 page +input-404 = "not-found.md" [output.html.fold] enable = true # whether or not to enable section folding diff --git a/documentation/dev-portal/custom.css b/documentation/dev-portal/custom.css new file mode 100644 index 0000000000..855f5eef5f --- /dev/null +++ b/documentation/dev-portal/custom.css @@ -0,0 +1,37 @@ +:root { + --mono-font: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace; +} +.coal { + --bg: #121726; + --fg: #f2f2f2; + + --sidebar-bg: #121726; + --sidebar-fg: #f2f2f2; + --sidebar-active: #fb6e4e; + + --icons: #f2f2f2; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +.light { + --bg: #f4f6f8; + --fg: #121726; + + --sidebar-bg: #f4f6f8; + --sidebar-fg: #121726; + --sidebar-active: #fb6e4e; + + --icons: #121726; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +footer { + font-size: 0.8em; + text-align: center; + border-top: 1px solid black; + padding: 5px 0; +} diff --git a/documentation/dev-portal/src/SUMMARY.md b/documentation/dev-portal/src/SUMMARY.md index 0e0c02bc58..8184038624 100644 --- a/documentation/dev-portal/src/SUMMARY.md +++ b/documentation/dev-portal/src/SUMMARY.md @@ -45,10 +45,9 @@ - [Community Applications and Guides](community-resources/community-applications-and-guides.md) - [Change Service Grantee Information](info-request.md) - [Rewards FAQ](community-resources/rewards-faq.md) ---- +--- # Misc. -- [Glossary](glossary.md) - [Code of Conduct](coc.md) - [Licensing](licensing.md) --- diff --git a/documentation/dev-portal/src/info-request.md b/documentation/dev-portal/src/info-request.md index 551da83bf3..e3edbdc3de 100644 --- a/documentation/dev-portal/src/info-request.md +++ b/documentation/dev-portal/src/info-request.md @@ -1,6 +1,6 @@ # Change Service Grantee Information -The Nym Service Grantees User Information Update Form allows Service Grantees to update their information with the Nym team. The form allows grantees to update their current: +If you wish to update any of the following information: - `Email Address` - `NYM address` - `Network Requester` @@ -9,10 +9,6 @@ The Nym Service Grantees User Information Update Form allows Service Grantees to - `Service` - `Payment Address` -Complete and submit your form so the Nym team to keep track of the service grantees and your current information, ensuring that deliver payments and handle other logistics smooth and accurately. - -> ⚠️ Service Grantee User Information will be updated by our team by 5pm UTC between Mon - Fri. - - +Please reach out either in the `#service-grantees` channel on Discord or on [Matrix](https://matrix.to/#/#nym-community:nymtech.chat). diff --git a/documentation/dev-portal/src/not-found.md b/documentation/dev-portal/src/not-found.md new file mode 100644 index 0000000000..704da5ec19 --- /dev/null +++ b/documentation/dev-portal/src/not-found.md @@ -0,0 +1,11 @@ +# Page Not Found + +You seem to have followed a link to a page that no longer exists. Our documentation is changing and growing over time, so this page has most likely been moved somewhere else. + +For node setup guides, see the [Operator Guides book](https://nymtech.net/operators). + +For information on network architecture traffic flow, clients, the SDKs, or interacting with the Nyx blockchain see the [Technical Documentation](https://nymtech.net/docs). + +If you are looking for information on developer tutorials, app guides, and demo and community apps, check the sitemap on the left hand side of the screen. + +If you still can't find what you're looking for get in touch with us via [Matrix](https://matrix.to/#/#nym-community:nymtech.chat). \ No newline at end of file diff --git a/documentation/dev-portal/theme/css/general.css b/documentation/dev-portal/theme/css/general.css index 84dbfa3371..f8597e5767 100644 --- a/documentation/dev-portal/theme/css/general.css +++ b/documentation/dev-portal/theme/css/general.css @@ -22,7 +22,7 @@ body { } code { - font-family: var(--mono-font) !important; + font-family: var(--mono-font); font-size: 0.9em; } diff --git a/documentation/docs/book.toml b/documentation/docs/book.toml index 996a7ec4e4..6d22440ee3 100644 --- a/documentation/docs/book.toml +++ b/documentation/docs/book.toml @@ -49,7 +49,7 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` [preprocessor.variables.variables] minimum_rust_version = "1.66" # vars for links: TODO think on how to streamline updating -platform_release_version = "v1.1.25" +platform_release_version = "v1.1.27" wallet_release_version = "v1.2.7" [preprocessor.last-changed] @@ -85,14 +85,14 @@ curly-quotes = true # mathjax-support = false # useful if we want to pull equations in copy-fonts = true no-section-label = false -additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "last-changed.css"] +additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "./custom.css"] additional-js = ["theme/pagetoc.js"] git-repository-url = "https://github.com/nymtech/nym" git-repository-icon = "fa-github" # edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" # site-url = "/docs/" # cname = "nymtech.net" -# input-404 = "not-found.md" # todo make custom 404 page +input-404 = "not-found.md" [output.html.fold] enable = true # whether or not to enable section folding @@ -119,5 +119,5 @@ expand = true # partial words will match longer terms heading-split-level = 3 # link results to heading levels copy-js = true # include Javascript code for search -# [output.linkcheck] -# warning-policy = "warn" +[output.linkcheck] +warning-policy = "warn" diff --git a/documentation/docs/custom.css b/documentation/docs/custom.css new file mode 100644 index 0000000000..855f5eef5f --- /dev/null +++ b/documentation/docs/custom.css @@ -0,0 +1,37 @@ +:root { + --mono-font: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace; +} +.coal { + --bg: #121726; + --fg: #f2f2f2; + + --sidebar-bg: #121726; + --sidebar-fg: #f2f2f2; + --sidebar-active: #fb6e4e; + + --icons: #f2f2f2; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +.light { + --bg: #f4f6f8; + --fg: #121726; + + --sidebar-bg: #f4f6f8; + --sidebar-fg: #121726; + --sidebar-active: #fb6e4e; + + --icons: #121726; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +footer { + font-size: 0.8em; + text-align: center; + border-top: 1px solid black; + padding: 5px 0; +} diff --git a/documentation/docs/src/SUMMARY.md b/documentation/docs/src/SUMMARY.md index 8d9c174f5a..c9fc9c6d1d 100644 --- a/documentation/docs/src/SUMMARY.md +++ b/documentation/docs/src/SUMMARY.md @@ -14,12 +14,11 @@ # Nodes -- [Node Setup Guides](nodes/setup-guides.md) - - [Mix Node](nodes/mix-node-setup.md) - - [Gateway](nodes/gateway-setup.md) - - [Network Requester](nodes/network-requester-setup.md) - - [Validator](nodes/validator-setup.md) -- [Troubleshooting](nodes/troubleshooting.md) +- [Node Types (Previously Setup Guides)](nodes/overview.md) + - [Mix Node](nodes/mixnode.md) + - [Gateway](nodes/gateway.md) + - [Network Requester](nodes/network-requester.md) + - [Validator](nodes/validator.md) # Clients - [Clients Overview](clients/overview.md) @@ -40,7 +39,7 @@ - [Mixnet Explorer](explorers/mixnet-explorer.md) # Nyx Blockchain - +- [Interacting with Nyx Chain and Smart Contracts](nyx/interacting-with-chain.md) - [Smart Contracts](nyx/smart-contracts.md) - [Mixnet Contract](nyx/mixnet-contract.md) - [Vesting Contract](nyx/vesting-contract.md) diff --git a/documentation/docs/src/architecture/network-overview.md b/documentation/docs/src/architecture/network-overview.md index fab4655e09..3a69f78be7 100644 --- a/documentation/docs/src/architecture/network-overview.md +++ b/documentation/docs/src/architecture/network-overview.md @@ -26,7 +26,7 @@ The mixnet - the different pieces of software that your traffic will pass throug * **Gateways** act as message storage for clients which may go offline and come back online again, and defend against denial of service attacks. The default gateway implementation included in the Nym platform code holds packets for later retrieval. For many applications (such as simple chat), this is usable out of the box, as it provides a place that potentially offline clients can retrieve packets from. The access token allows clients to pull messages from the gateway node. -* **Services** are applications that communicate with nym clients, listening and sending traffic to the mixnet. This is an umbrella term for a variety of different pieces of code, such as the [network requester](../nodes/network-requester-setup.md) binary. +* **Services** are applications that communicate with nym clients, listening and sending traffic to the mixnet. This is an umbrella term for a variety of different pieces of code, such as the [network requester](../nodes/network-requester.md) binary. * **Nyx Blockchain Validators** secure the network with proof-of-stake Sybil defenses, determine which nodes are included within the network, and work together to create Coconut threshold credentials which provide anonymous access to data and resources. They also produce blocks and secure the Nyx Blockchain. Initially, this chain was used only to house the CosmWasm smart contracts keeping track of Nym's network topology, token vesting contracts, and the `NYM` token itself. In recent months, we've decided to expand the role of Nyx and instead expand its role by making it an open smart contract platform for anyone to upload CosmWasm smart contracts to. Validators also provide privacy-enhanced credentials based on the testimony of a set of decentralized, blockchain-based issuing authorities. Nym validators use the [Coconut](https://arxiv.org/abs/1802.07344) [signature scheme](https://en.wikipedia.org/wiki/Digital_signature) to issue credentials. This allows privacy apps to generate anonymous resource claims through decentralised authorities, then use them with Service Providers. diff --git a/documentation/docs/src/binaries/building-nym.md b/documentation/docs/src/binaries/building-nym.md index 06edd54a2f..a79af72c3d 100644 --- a/documentation/docs/src/binaries/building-nym.md +++ b/documentation/docs/src/binaries/building-nym.md @@ -8,7 +8,7 @@ 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. -> This page details how to build the main Nym platform code. **If you want to build and run a validator, [go here](../nodes/validator-setup.md) instead.** +> This page details how to build the main Nym platform code. **If you want to build and run a validator, [go here](../nodes/validator.md) instead.** ## Prerequisites - Debian/Ubuntu: `pkg-config`, `build-essential`, `libssl-dev`, `curl`, `jq`, `git` @@ -55,11 +55,11 @@ NETWORK=sandbox cargo build --release # build your binaries with **sandbox** con Quite a bit of stuff gets built. The key working parts are: -* [mix node](../nodes/mix-node-setup.md): `nym-mixnode` -* [gateway node](../nodes/gateway-setup.md): `nym-gateway` +* [mix node](../nodes/mixnode.md): `nym-mixnode` +* [gateway node](../nodes/gateway.md): `nym-gateway` * [websocket client](../clients/websocket-client.md): `nym-client` * [socks5 client](../clients/socks5-client.md): `nym-socks5-client` -* [network requester](../nodes/network-requester-setup.md): `nym-network-requester` +* [network requester](../nodes/network-requester.md): `nym-network-requester` * [nym-cli tool](../tools/nym-cli.md): `nym-cli` The repository also contains Typescript applications which aren't built in this process. These can be built by following the instructions on their respective docs pages. diff --git a/documentation/docs/src/clients/overview.md b/documentation/docs/src/clients/overview.md index 4bfc125b5c..c8d2db1503 100644 --- a/documentation/docs/src/clients/overview.md +++ b/documentation/docs/src/clients/overview.md @@ -23,19 +23,23 @@ At present, there are three Nym clients: You need to choose which one you want incorporate into your app. Which one you use will depend largely on your preferred programming style and the purpose of your app. ### The websocket client -Your first option is the native websocket client (`nym-client`). This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It runs as a persistent process on a desktop or server machine. You can connect to it with **any language that supports websockets**. +Your first option is the native websocket client (`nym-client`). This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It can be run as a persistent process on a desktop or server machine. You can connect to it with **any language that supports websockets**. + +_Rust developers can import websocket client functionality into their code via the [Rust SDK](../sdk/rust.md)_. ### The webassembly client If you're working in JavaScript or Typescript in the browser, or building an [edge computing](https://en.wikipedia.org/wiki/Edge_computing) app, you'll likely want to choose the webassembly client. It's packaged and [available on the npm registry](https://www.npmjs.com/package/@nymproject/nym-client-wasm), so you can `npm install` it into your JavaScript or TypeScript application. -The webassembly client is most easily used via the [sdk](../sdk/typescript.md). +_The webassembly client is most easily used via the [Typescript SDK](../sdk/typescript.md)_. ### The SOCKS5 client -This client (`nym-socks5-client`) is useful for allowing existing applications to use the Nym mixnet without any code changes. All that's necessary is that they can use one of the SOCKS5, SOCKS4a, or SOCKS4 proxy protocols (which many applications can - crypto wallets, browsers, chat applications etc). +The `nym-socks5-client` is useful for allowing existing applications to use the Nym mixnet without any code changes. All that's necessary is that they can use one of the SOCKS5, SOCKS4a, or SOCKS4 proxy protocols (which many applications can - crypto wallets, browsers, chat applications etc). -It's less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes. +When used as a standalone client, it's less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes. + +_Rust developers can import socks client functionality into their code via the [Rust SDK](../sdk/rust.md)_. ## Commonalities between clients All Nym client packages present basically the same capabilities to the privacy application developer. They need to run as a persistent process in order to stay connected and ready to receive any incoming messages from their gateway nodes. They register and authenticate to gateways, and encrypt Sphinx packets. diff --git a/documentation/docs/src/clients/socks5-client.md b/documentation/docs/src/clients/socks5-client.md index e7163347cb..e3a5759685 100644 --- a/documentation/docs/src/clients/socks5-client.md +++ b/documentation/docs/src/clients/socks5-client.md @@ -60,7 +60,7 @@ There are 2 pieces of software that work together to send SOCKS traffic through The `nym-socks5-client` allows you to do the following from your local machine: * Take a TCP data stream from a application that can send traffic via SOCKS5. * Chop up the TCP stream into multiple Sphinx packets, assigning sequence numbers to them, while leaving the TCP connection open for more data -* Send the Sphinx packets through the mixnet to a [network requester](../nodes/network-requester-setup.md). Packets are shuffled and mixed as they transit the mixnet. +* Send the Sphinx packets through the mixnet to a [network requester](../nodes/network-requester.md). Packets are shuffled and mixed as they transit the mixnet. The `nym-network-requester` then reassembles the original TCP stream using the packets' sequence numbers, and make the intended request. It will then chop up the response into Sphinx packets and send them back through the mixnet to your `nym-socks5-client`. The application will then receive its data, without even noticing that it wasn't talking to a "normal" SOCKS5 proxy! @@ -82,7 +82,7 @@ You can check that your binaries are properly compiled with: You can check the necessary parameters for the available commands by running: ``` -./nym-client --help +./nym-client --help ``` ### Initialising a new client instance @@ -100,9 +100,9 @@ Before you can use the client, you need to initalise a new instance of it, which The `--id` in the example above is a local identifier so that you can name your clients and keep track of them on your local system; it is **never** transmitted over the network. -The `--provider` field needs to be filled with the Nym address of a Network Requester that can make network requests on your behalf. If you don't want to [run your own](../nodes/network-requester-setup.md) you can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/service-providers) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [this list](https://harbourmaster.nymtech.net/). +The `--provider` field needs to be filled with the Nym address of a Network Requester that can make network requests on your behalf. If you don't want to [run your own](../nodes/network-requester.md) you can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/service-providers) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [this list](https://harbourmaster.nymtech.net/). -Since the nodes on this list are the infrastructure for [Nymconnect](https://nymtech.net/developers/quickstart/nymconnect-gui.html) they will support all apps on the [default whitelist](../nodes/network-requester-setup.md#network-requester-whitelist): Keybase, Telegram, Electrum, Blockstream Green, and Helios. +Since the nodes on this list are the infrastructure for [Nymconnect](https://nymtech.net/developers/quickstart/nymconnect-gui.html) they will support all apps on the [default whitelist](../nodes/network-requester.md#network-requester-whitelist): Keybase, Telegram, Electrum, Blockstream Green, and Helios. #### Choosing a Gateway By default - as in the example above - your client will choose a random gateway to connect to. @@ -169,3 +169,13 @@ Here is an example of setting the proxy connecting in Blockstream Green: Most wallets and other applications will work basically the same way: find the network proxy settings, enter the proxy url (host: **localhost**, port: **1080**). In some other applications, this might be written as **localhost:1080** if there's only one proxy entry field. + +## Useful Commands + +**no-banner** + +Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +**build-info** + +A `build-info` command prints the build information like commit hash, rust version, binary version just like what command `--version` does. However, you can also specify an `--output=json` flag that will format the whole output as a json, making it an order of magnitude easier to parse. diff --git a/documentation/docs/src/images/nym-token-flow.png b/documentation/docs/src/images/nym-token-flow.png new file mode 100644 index 0000000000..eb026e2325 Binary files /dev/null and b/documentation/docs/src/images/nym-token-flow.png differ diff --git a/documentation/docs/src/introduction.md b/documentation/docs/src/introduction.md index a6cf38aae7..9964ec5b75 100644 --- a/documentation/docs/src/introduction.md +++ b/documentation/docs/src/introduction.md @@ -2,27 +2,29 @@ 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, how to integrate with the network, developer tutorials and quickstart guides, check out the [Developer Portal](https://nymtech.net/developers/). - -If you are looking for information on grants and beta-release Nym apps, check out the [Shipyard](https://shipyard.nymtech.net) site. +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 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/). ## Popular pages **Network Architecture:** * [Network Overview](./architecture/network-overview.md) * [Mixnet Traffic Flow](./architecture/traffic-flow.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) +**SDK examples:** +* [Typescript SDK](./sdk/typescript.md) +* [Rust SDK](./sdk/rust.md) + +**Nyx** +* [Interacting with the Nyx chain](./nyx/interacting-with-chain.md) +* [Ledger Live setup](./nyx/ledger-live.md) +* [RPC Node](./nyx/rpc-node.md) **Client setup and usage guides:** * [Websocket client](./clients/websocket-client.md) * [Socks5 client](./clients/socks5-client.md) * [Webassembly client](./clients/webassembly-client.md) -**SDK guides:** -* [Typescript SDK](./sdk/typescript.md) -* [Rust SDK](./sdk/rust.md) +**Wallets** +* [Desktop](./wallet/desktop-wallet.md) +* [CLI](./wallet/cli-wallet.md) \ No newline at end of file diff --git a/documentation/docs/src/nodes/gateway-setup.md b/documentation/docs/src/nodes/gateway-setup.md deleted file mode 100644 index a933dc5b04..0000000000 --- a/documentation/docs/src/nodes/gateway-setup.md +++ /dev/null @@ -1,260 +0,0 @@ -# Gateways - -> The Nym gateway 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. - -## Current version -``` - -``` - - -## Preliminary steps -There are a couple of steps that need completing before starting to set up your gateway: - -- preparing your wallet -- requisitioning a VPS (Virtual Private Server) - -### Wallet preparation -#### Mainnet -Before you initialise and run your gateway, head to our [website](https://nymtech.net/download/) and download the Nym wallet for your operating system. If pre-compiled binaries for your operating system aren't availiable, you can build the wallet yourself with instructions [here](../wallet/desktop-wallet.md). - -If you don't already have one, please create a Nym address using the wallet, and fund it with tokens. The minimum amount required to bond a gateway is 100 `NYM`, but make sure you have a bit more to account for gas costs. - -`NYM` can be purchased via Bity from the wallet itself with BTC or fiat, and is currently present on several exchanges. - -> Remember that you can **only** use native Cosmos `NYM` tokens to bond your gateway. You **cannot** use ERC20 representations of `NYM` to run a node. - - -#### Sandbox testnet -Make sure to download a wallet and create an account as outlined above. Then head to our [token faucet](https://faucet.nymtech.net/) and get some tokens to use to bond it. - -### VPS Hardware Specs -You will need to rent a VPS to run your mix node on. One key reason for this is that your node **must be able to send TCP data using both IPv4 and IPv6** (as other nodes you talk to may use either protocol. - -We currently have these _rough_ specs for VPS hardware: - -- Processors: 2 cores are fine. Get the fastest CPUs you can afford. -- RAM: Memory requirements depend on the amount of users your Gateway will be serving at any one time. If you're just going to be using it yourself, then minimal RAM is fine. **If you're running your Gateway as part of a Service Grant, get something with at least 4GB RAM.** -- Disks: much like the amount of RAM your Gateway could use, the amount of disk space required will vary with the amount of users your Gateway is serving. **If you're running your Gateway as part of a Service Grant, get something with at least 40GB storage.** - -## Gateway setup -Now that you have built the codebase, set up your wallet, and have a VPS with the `nym-gateway` binary, you can set up your gateway with the instructions below. - -### Viewing command help -You can check that your binaries are properly compiled with: - -``` -./nym-gateway --help -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -You can also check the various arguments required for individual commands with: - -``` -./nym-gateway --help -``` - -### Initialising your gateway -To check available configuration options use: - -``` - ./nym-gateway init --help -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -The following command returns a gateway on your current IP with the `id` of `supergateway`: - -``` -./nym-gateway init --id supergateway --host $(curl ifconfig.me) --wallet-address n1eufxdlgt0puwrwptgjfqne8pj4nhy2u5ft62uq -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -The `$(curl ifconfig.me)` command above returns your IP automatically using an external service. Alternatively, you can enter your IP manually wish. If you do this, remember to enter your IP **without** any port information. - -### Bonding your gateway -#### Via the Desktop wallet -You can bond your gateway via the Desktop wallet. - -* Open your wallet, and head to the `Bond` page, then select the node type and input your node details. Press `continue` - -* 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. It will look something like this: - -~~~admonish example collapsible=true title="Console output" -``` -./nym-gateway sign --id upgrade_test --contract-msg 2Mf8xYytgEeyJke9LA7TjhHoGQWNBEfgHZtTyy2krFJfGHSiqy7FLgTnauSkQepCZTqKN5Yfi34JQCuog9k6FGA2EjsdpNGAWHZiuUGDipyJ6UksNKRxnFKhYW7ri4MRduyZwbR98y5fQMLAwHne1Tjm9cXYCn8McfigNt77WAYwBk5bRRKmC34BJMmWcAxphcLES2v9RdSR68tkHSpy2C8STfdmAQs3tZg8bJS5Qa8pQdqx14TnfQAPLk3QYCynfUJvgcQTrg29aqCasceGRpKdQ3Tbn81MLXAGAs7JLBbiMEAhCezAr2kEN8kET1q54zXtKz6znTPgeTZoSbP8rzf4k2JKHZYWrHYF9JriXepuZTnyxAKAxvGFPBk8Z6KAQi33NRQkwd7MPyttatHna6kG9x7knffV6ebGzgRBf7NV27LurH8x4L1uUXwm1v1UYCA1WSBQ9Pp2JW69k5v5v7G9gBy8RUcZnMbeL26Qqb8WkuGcmuHhaFfoqSfV7PRHPpPT4M8uRqUyR4bjUtSJJM1yh6QSeZk9BEazzoJqPeYeGoiFDZ3LMj2jesbJweQR4caaYuRczK92UGSSqu9zBKmE45a - - - _ __ _ _ _ __ ___ - | '_ \| | | | '_ \ _ \ - | | | | |_| | | | | | | - |_| |_|\__, |_| |_| |_| - |___/ - - (nym-gateway - version {{platform_release_version}}) - - ->>> 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"}}}} -``` -~~~ - -* Copy the resulting signature: - -``` ->>> The base58-encoded signature is: -2SPDjLjX4b6XEtkgG7yD8Znsb1xycL1edFvRK4JcVnPsM9k6HXEUUeVS6rswRiYxoj1bMgiRKyPDwiksiuyxu8Xi -``` - -* And paste it into the wallet nodal, then confirm the transaction. - -![Paste Signature](../images/wallet-sign.png) - -* 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](../tools/nym-cli.md#bond-a-gateway) docs. - -### Running your gateway -The `run` command starts the gateway: - -``` -./nym-gateway run --id supergateway -``` - -### Upgrading your gateway -Upgrading your node is a two-step process: -* Updating the binary and `config.toml` on your VPS -* Updating the node information in the [mixnet smart contract](../nyx/mixnet-contract.md). **This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net)**. - ->These instructions are specifically regarding upgrading your gateway binary from one version to another. If you want to change node information such as the listening port, you can do this by clicking the `node settings` tab in the `bond` page of the wallet. - -#### Step 1: upgrading your binary -Follow these steps to upgrade your binary and update its config file: -* pause your gateway process. -* replace the existing binary with the newest binary (which you can either compile yourself 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 gateway process with the new binary. - -#### Step 2: updating your node information in the smart contract -Follow these steps to update the information about your node which is publically avaliable 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 -* Navigate to the `Bonding` page and click the `Node Settings` link in the top right corner: -![Bonding page](../images/wallet-screenshots/bonding.png) - -* Update the fields in the `Node Settings` page and click `Submit changes to the blockchain`. -![Node Settings Page](../images/wallet-screenshots/node_settings.png) - -#### Updating node information via the CLI -If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](../tools/nym-cli.md#upgrade-a-mix-node) docs. - -## VPS Setup and Automation -### Configure your firewall -Although your gateway is now ready to receive traffic, your server may not be - the following commands will allow you to set up a properly configured firewall using `ufw`: - -``` -# check if you have ufw installed -ufw version -# if it is not installed, install with -sudo apt install ufw -y -# enable ufw -sudo ufw enable -# check the status of the firewall -sudo ufw status -``` - -Finally open your gateway's p2p port, as well as ports for ssh and incoming traffic connections: - -``` -sudo ufw allow 1789,22,9000/tcp -# check the status of the firewall -sudo ufw status -``` - -For more information about your gateway's port configuration, check the [gateway port reference table](#gateway-port-reference) below. - -### Automating your gateway with systemd -Although it's not totally necessary, it's useful to have the gateway automatically start at system boot time. Here's a systemd service file to do that: - -```ini -[Unit] -Description=Nym Gateway ({{platform_release_version}}) -StartLimitInterval=350 -StartLimitBurst=10 - -[Service] -User=nym -LimitNOFILE=65536 -ExecStart=/home/nym/nym-gateway run --id supergateway -KillSignal=SIGINT -Restart=on-failure -RestartSec=30 - -[Install] -WantedBy=multi-user.target -``` - -Put the above file onto your system at `/etc/systemd/system/nym-gateway.service`. - -Change the path in `ExecStart` to point at your gateway binary (`nym-gateway`), and the `User` so it is the user you are running as. - -If you have built nym on your server, and your username is `jetpanther`, then the start command might look like this: - -`ExecStart=/home/jetpanther/nym/target/release/nym-gateway run --id your-id`. Basically, you want the full `/path/to/nym-gateway run --id whatever-your-node-id-is` - - -Then run: - -``` -systemctl enable nym-gateway.service -``` - -Start your node: - -``` -service nym-gateway start -``` - -This will cause your node to start at system boot time. If you restart your machine, the node will come back up automatically. - -You can also do `service nym-gateway stop` or `service nym-gateway restart`. - -Note: if you make any changes to your systemd script after you've enabled it, you will need to run: - -``` -systemctl daemon-reload -``` - -This lets your operating system know it's ok to reload the service configuration. - -## Gateway related Validator API endpoints -Numerous gateway related API endpoints are documented on the Validator API's [Swagger Documentation](https://validator.nymtech.net/api/swagger/index.html). There you can also try out various requests from your broswer, 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. - -## Ports -All gateway specific port configuration can be found in `$HOME/.nym/gateways//config/config.toml`. If you do edit any port configs, remember to restart your gateway. - -### Gateway port reference -| Default port | Use | -|--------------|---------------------------| -| 1789 | Listen for Mixnet traffic | -| 9000 | Listen for Client traffic | diff --git a/documentation/docs/src/nodes/gateway.md b/documentation/docs/src/nodes/gateway.md new file mode 100644 index 0000000000..3634c36652 --- /dev/null +++ b/documentation/docs/src/nodes/gateway.md @@ -0,0 +1,11 @@ +# Gateways + +> The gateway setup and maintenance guide has migrated to the [Operator Guides book](https://nymtech.net/operators/nodes/gateway-setup.html). + +Gateways are key to both the usability of the mixnet, and the operation of the mixnet's tokenomics. They serve two main functions: +* In the future (when the mixnet is no longer running in 'free to use' mode), to check for zkNym credentials (previously referred to as [Coconut Credentials](../bandwidth-credentials.md)) with which users can anonymously prove they have paid to send traffic through the mixnet. A % of the worth of these credentials will be distributed to the operator of the gateway periodically as payment for providing their service. The more credentials user clients 'spend' with them, the higher the rewards for the gateway operator and their delegators will be. The rest of this value will be sent to the Mixmining Pool, a pool of tokens from which `NYM` rewards are distributed to mix node operators. +* Act as a mailbox for connected clients. Clients create a lasting relationship with a gateway on initialisation, binding themselves to always use a particular gateway as their ingress point for mixnet traffic (this also means that this gateway is the egress point for any traffic sent _to_ this client - see the [mixnet traffic flow page](../architecture/traffic-flow.md) and the [addressing scheme](../clients/addressing-system.md) for further details). If a client is offline and the Gateway can't deliver packets addressed to it, they will hold these packets until the client comes back online. + +## Further Reading +* [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) section 4.2 +* [Nym Blog: Gateways to Privacy](https://blog.nymtech.net/gateways-to-privacy-51196005bf5) diff --git a/documentation/docs/src/nodes/mix-node-setup.md b/documentation/docs/src/nodes/mix-node-setup.md deleted file mode 100644 index 4635aa56ac..0000000000 --- a/documentation/docs/src/nodes/mix-node-setup.md +++ /dev/null @@ -1,564 +0,0 @@ -# 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. - -## Current version -``` - -``` - -The `nym-mixnode` binary is currently one point version ahead of the rest of the platform binaries due to a patch applied between releases. - -## Preliminary steps - -There are a couple of steps that need completing before starting to set up your mix node: - -- preparing your wallet -- requisitioning a VPS (Virtual Private Server) - -### Wallet preparation - -#### Mainnet - -Before you initialise and run your mixnode, head to our [website](https://nymtech.net/download/) and download the Nym wallet for your operating system. If pre-compiled binaries for your operating system aren't availiable, you can build the wallet yourself with instructions [here](../wallet/desktop-wallet.md). - -If you don't already have one, please create a Nym address using the wallet, and fund it with tokens. The minimum amount required to bond a mixnode is 100 `NYM`, but make sure you have a bit more to account for gas costs. - -`NYM` can be purchased via Bity from the wallet itself with BTC or fiat, and is currently present on several exchanges. - -> Remember that you can **only** use Cosmos `NYM` tokens to bond your mixnode. You **cannot** use ERC20 representations of `NYM` to run a node. - -#### Sandbox testnet - -Make sure to download a wallet and create an account as outlined above. Then head to our [token faucet](https://faucet.nymtech.net/) and get some tokens to use to bond it. - -### VPS Hardware Specs - -You will need to rent a VPS to run your mix node on. One key reason for this is that your node **must be able to send TCP data using both IPv4 and IPv6** (as other nodes you talk to may use either protocol). - -For the moment, we haven't put a great amount of effort into optimizing concurrency to increase throughput, so don't bother provisioning a beastly server with multiple cores. This will change when we get a chance to start doing performance optimizations in a more serious way. Sphinx packet decryption is CPU-bound, so once we optimise, more fast cores will be better. - -For now, see the below rough specs: - -- Processors: 2 cores are fine. Get the fastest CPUs you can afford. -- RAM: Memory requirements are very low - typically a mix node may use only a few hundred MB of RAM. -- Disks: The mixnodes require no disk space beyond a few bytes for the configuration files. - -## Mix node setup - -Now that you have built the codebase, set up your wallet, and have a VPS with the `nym-mixnode` binary, you can set up your mix node with the instructions below. - -### Viewing command help - -You can check that your binaries are properly compiled with: - -``` -./nym-mixnode --help -``` - -Which should return a list of all avaliable commands. - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -You can also check the various arguments required for individual commands with: - -``` -./nym-mixnode --help -``` - -### Initialising your mix node - -To check available configuration options for initializing your node use: - -``` -./nym-mixnode init --help -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -Initalise your mixnode with the following command, replacing the value of `--id` with the moniker you wish to give your mixnode. 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 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 winston-smithnode --host $(curl ifconfig.me) -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -> 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//`. - -### Bonding your mix node - -```admonish caution -From `v1.1.3`, if you unbond your mixnode 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 and input your node details. Press `continue` - -* 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. It will look something like this: - -~~~admonish example collapsible=true title="Console output" -``` -./nym-mixnode sign --id upgrade_test --contract-msg 5XrvVEMzRJk2AcT2h1o6ErZNb8z1ZzD3h7teipBW3NUtrtYq7vu4DRMgzZRTPVPnyr2YWCxpmKCMFaEXvksnJ -4jt7np3NMLxsLMrFjEBhh67Crtjy4868vCzAivUqzdc365RiqxQQKtv4r9eTk9mTbE9JY8U3TxzKJCSGcBqbrb9JX3HrZVWm6tqbUYbsnku9pqnfeyeUiaYKY44Lm72TYrkZfRrMAZLMATiXT1ntmiKqT37HzRxNZjiH8qHeQEoRHkgDsmXDXRbfppGTpPrN7R4sjynJzehzUBZ8Ug7ovT9FoAHb8kuVQhUiMs1js6tdwtthzQMbPi9vwxUtVvjYknN2fnJgMnckEhzJJpJDCNdH7YhpPaWQnGVVS334mskiuqkbRVrFPJN2nnwArHr3L2cLxSMk9toKfw7ViKJ2p5E5JxiSmKY1cFGZ7uRLsuQ833PJN9JE8crPtkBNefqkbFNz68S5jPmzUShSvAc4TqXKeovDASFmmhKaPqLUrfsSWm7nzuKnzJSMADF6xSuwr9cknMoirqkRkLe7ybJ2ERwSdf5cUxMjF7yjS8tW9hZudnTUb1uPNDuSmPPVrCR12XZyFzBvVgxH51ZNJTym46nqnfA881LQcmFMnCwJf39rVJ4ASLnzEzmuwXj75QoB9ce9kiLmoBNLYe4QKSB6gDd858VnBtBNQELVuCCZbrTYuSCeNdUFhvMwD4kryc1pBYUa8Ro81F3QVfiKN - - _ __ _ _ _ __ ___ - | '_ \| | | | '_ \ _ \ - | | | | |_| | | | | | | - |_| |_|\__, |_| |_| |_| - |___/ - - (nym-mixnode - version {{mix_node_release_version}}) - - ->>> attempting to sign 5XrvVEMzRJk2AcT2h1o6ErZNb8z1ZzD3h7teipBW3NUtrtYq7vu4DRMgzZRTPVPnyr2YWCxpmKCMFaEXvksnJ4jt7np3NMLxsLMrFjEBhh67Crtjy4868vCzAivUqzdc365RiqxQQKtv4r9eTk9mTbE9JY8U3TxzKJCSGcBqbrb9JX3HrZVWm6tqbUYbsnku9pqnfeyeUiaYKY44Lm72TYrkZfRrMAZLMATiXT1ntmiKqT37HzRxNZjiH8qHeQEoRHkgDsmXDXRbfppGTpPrN7R4sjynJzehzUBZ8Ug7ovT9FoAHb8kuVQhUiMs1js6tdwtthzQMbPi9vwxUtVvjYknN2fnJgMnckEhzJJpJDCNdH7YhpPaWQnGVVS334mskiuqkbRVrFPJN2nnwArHr3L2cLxSMk9toKfw7ViKJ2p5E5JxiSmKY1cFGZ7uRLsuQ833PJN9JE8crPtkBNefqkbFNz68S5jPmzUShSvAc4TqXKeovDASFmmhKaPqLUrfsSWm7nzuKnzJSMADF6xSuwr9cknMoirqkRkLe7ybJ2ERwSdf5cUxMjF7yjS8tW9hZudnTUb1uPNDuSmPPVrCR12XZyFzBvVgxH51ZNJTym46nqnfA881LQcmFMnCwJf39rVJ4ASLnzEzmuwXj75QoB9ce9kiLmoBNLYe4QKSB6gDd858VnBtBNQELVuCCZbrTYuSCeNdUFhvMwD4kryc1pBYUa8Ro81F3QVfiKN ->>> 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, then confirm the transaction. - -![Paste Signature](../images/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](../tools/nym-cli.md#bond-a-mix-node) docs. - -### Running your mix node - -Now you've bonded your mix node, run it with: - -``` -./nym-mixnode run --id winston-smithnode -``` - -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. - -Keep reading to find out more about configuration options or troubleshooting if you're having issues. There are also some tips for running on AWS and other cloud providers, some of which require minor additional setup. - -Also have a look at the saved configuration files in `$HOME/.nym/mixnodes/` to see more configuration options. - -### Describe your mix node (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 mixnode with the following command: - -``` -./nym-mixnode describe --id winston-smithnode -``` - -> Remember to restart your mix node process in order for the new description to be propogated. - -### Upgrading your mix node - -Upgrading your node is a two-step process: -* Updating the binary and `config.toml` on your VPS -* Updating the node information in the [mixnet smart contract](../nyx/mixnet-contract.md). **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 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. - -#### Step 2: updating your node information in the smart contract -Follow these steps to update the information about your mix node which is publically avaliable 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 -* Navigate to the `Bonding` page and click the `Node Settings` link in the top right corner: -![Bonding page](../images/wallet-screenshots/bonding.png) - -* Update the fields in the `Node Settings` page and click `Submit changes to the blockchain`. -![Node Settings Page](../images/wallet-screenshots/node_settings.png) - -#### Updating node information via the CLI -If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](../tools/nym-cli.md#upgrade-a-mix-node) docs. - -### Displaying mix node information - -You can always check the details of your mix node with the `node-details` command: - -``` -./nym-mixnode node-details --id winston-smithnode -``` - -## VPS Setup and Automation -### Configure your firewall -The following commands will allow you to set up a firewall using `ufw`. - -``` -# check if you have ufw installed -ufw version -# if it is not installed, install with -sudo apt install ufw -y -# enable ufw -sudo ufw enable -# check the status of the firewall -sudo ufw status -``` - -Finally open your mix node's p2p port, as well as ports for ssh and ports `8000` and `1790` for verloc and measurement pings: - -``` -sudo ufw allow 1789,1790,8000,22/tcp -# check the status of the firewall -sudo ufw status -``` - -For more information about your mix node's port configuration, check the [mix node port reference table](./mix-node-setup.md#mixnode-port-reference) below. - -### Automating your mix node with systemd - -It's useful to have the mix node automatically start at system boot time. Here's a systemd service file to do that: - -```ini -[Unit] -Description=Nym Mixnode ({{mix_node_release_version}}) -StartLimitInterval=350 -StartLimitBurst=10 - -[Service] -User=nym -LimitNOFILE=65536 -ExecStart=/home/nym/nym-mixnode run --id mix0100 -KillSignal=SIGINT -Restart=on-failure -RestartSec=30 - -[Install] -WantedBy=multi-user.target -``` - -Put the above file onto your system at `/etc/systemd/system/nym-mixnode.service`. - -Change the path in `ExecStart` to point at your mix node binary (`nym-mixnode`), and the `User` so it is the user you are running as. - -If you have built nym on your server, and your username is `jetpanther`, then the start command might look like this: - -`ExecStart=/home/jetpanther/nym/target/release/nym-mixnode run --id your-id`. Basically, you want the full `/path/to/nym-mixnode run --id whatever-your-node-id-is` - -Then run: - -``` -systemctl enable nym-mixnode.service -``` - -Start your node: - -``` -service nym-mixnode start -``` - -This will cause your node to start at system boot time. If you restart your machine, the node will come back up automatically. - -You can also do `service nym-mixnode stop` or `service nym-mixnode restart`. - -Note: if you make any changes to your systemd script after you've enabled it, you will need to run: - -``` -systemctl daemon-reload -``` - -This lets your operating system know it's ok to reload the service configuration. - -#### Setting the ulimit - -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. - -If you see errors such as: - -``` -Failed to accept incoming connection - Os { code: 24, kind: Other, message: "Too many open files" } -``` - -This means that the operating system is preventing network connections from being made. - -##### Set the ulimit via `systemd` service file - -Query the `ulimit` of your mix node with: - -``` -grep -i "open files" /proc/$(ps -A -o pid,cmd|grep nym-mixnode | grep -v grep |head -n 1 | awk '{print $1}')/limits -``` - -You'll get back the hard and soft limits, which looks something like this: - -``` -Max open files 65536 65536 files -``` - -If your output is **the same as above**, your node will not encounter any `ulimit` related issues. - -However if either value is `1024`, you must raise the limit via the systemd service file. Add the line: - -``` -LimitNOFILE=65536 -``` - -Reload the daemon: - -``` -systemctl daemon-reload -``` - -or execute this as root for system-wide setting of `ulimit`: - -``` -echo "DefaultLimitNOFILE=65535" >> /etc/systemd/system.conf -``` - -Reboot your machine and restart your node. When it comes back, use `cat /proc/$(pidof nym-mixnode)/limits | grep "Max open files"` to make sure the limit has changed to 65535. - -##### Set the ulimit on `non-systemd` based distributions - -Edit `etc/security/conf` and add the following lines: - -``` -# Example hard limit for max opened files -username hard nofile 4096 -# Example soft limit for max opened files -username soft nofile 4096 -``` - -Then reboot your server and restart your mixnode. - -## Node Description -Node description is a short text that describes your node. It is displayed in the `nym-mixnode list` command and in the `nym-mixnode node-details` command. it also shows up in the node explorer to let people know what your node is about and link to your website. - -To set your node description, create a file called `description.toml` and put it in the same directory as your `config.toml` file. The file should look like this: - -```toml -name = "Winston Smith" -description = "I am the Sphinx" -link = "https://nymtech.net" -location = "Giza, Egypt" -``` - -you will need to restart your node for the changes to take effect. - -## 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. - -### 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. - -You can use either `nym-cli` which can be downloaded from the [release page](https://github.com/nymtech/nym/releases) or compiling `nyxd`. - - -`/path/to/the/release` and run the following on the family head to obtain the signature for the member: - -``` -./nym-mixnode sign --id winston-smithnode --text -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -Using `nym-cli`: - -> `--mnemonic` is the mnemonic of the member wanting to be the head of family. - -``` -/nym-cli cosmwasm execute '{"create_family": {"signature": "","family_head": "","owner_signature":"","label": ""}}' --mnemonic -``` - -Using `nyxd`: - -> `--from` is mnemonic of the member wanting to join the family. - -``` -./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block -``` - -To get the node owner signature, use: - -`./nym-mixnode node-details --id ` - -### Joining a Node Family - -`/path/to/the/release` and run the following on the family head to obtain the signature for the member: - -``` -./nym-mixnode sign --id mixnode --text -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -Using `nym-cli`: - -``` -./nym-cli cosmwasm execute '{"join_family": {"signature": "","family_head": "","owner_signautre": "", "label":""}}' --mnemonic -``` - -Using `nyxd`: - -``` -./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block -``` - - -To get the node owner signature, use: - -`./nym-mixnode node-details --id ` - - -### Leaving a family -If wanting to leave, run the same initial command as above, followed by: - -Using `nym-cli`: - -``` -./nym-cli cosmwasm execute n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr '{"leave_family": {"signature": "","family_head": "","owner_signautre": ""}}' --mnemonic -``` - -Using `nyxd`: - -``` -./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block -``` - -## 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: - -- [Mainnet](https://explorer.nymtech.net/overview) -- [Sandbox testnet](https://sandbox-explorer.nymtech.net/) - -Enter your **identity key** to find your node. There are numerous statistics about your node on that page that are useful for checking your uptime history, packets mixed, and any delegations your node may have. - -There are also 2 community explorers which have been created by [Nodes Guru](https://nodes.guru): - -- [Mainnet](https://mixnet.explorers.guru/) -- [Sandbox testnet](https://sandbox.mixnet.explorers.guru/) - -For more details see [Troubleshooting FAQ](../nodes/troubleshooting.md) - -### Virtual IPs and hosting via Google & AWS -On some services (AWS, Google, etc), the machine's available bind address is not the same as the public IP address. In this case, bind `--host` to the local machine address returned by `ifconfig`, but also specify `--announce-host` with the public IP. Please make sure that you pass the correct, routable `--announce-host`. - -For example, on a Google machine, you may see the following output from the `ifconfig` command: - -``` -ens4: flags=4163 mtu 1460 - inet 10.126.5.7 netmask 255.255.255.255 broadcast 0.0.0.0 - ... -``` - -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. - -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 `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. - -## Metrics / API endpoints -The mix node binary exposes several API endpoints that can be pinged in order to gather information about the node, and the Nym API (previously 'Validator API') exposes numerous mix node related endpoints which provide network-wide information about mix nodes, the network topology (the list of avaliable mix nodes for packet routing), and information regarding uptime monitoring and rewarding history. - -### Mix node API endpoints -Since the mix node binary exposes several API endpoints itself, you can ping these easily via curl: - -| Endpoint | Description | Command | -| -------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -| `/description` | Returns the description of the node set with the `describe` command | `curl :8000/description` | -| `/hardware` | Returns the hardware information of the node | `curl :8000/hardware` | -| `/verloc` | Returns the verloc information of the node, updated every 12 hours | `curl :8000/verloc` | - -The code for exposed API endpoints can be found [here](https://github.com/nymtech/nym/tree/release/{{platform_release_version}}/mixnode/src/node/http). - -> You can get more detailed info by appending `?debug` to the URL, like so: `curl http://:8000/stats?debug` - -### Mix node related Nym API (previously 'Validator API') endpoints -Numerous 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 broswer, 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 - -The Reward Estimation API endpoint allows mix node operators to estimate the rewards they could earn for running a Nym mixnode 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 mixnode, 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: - -``` -/status/mixnode/{mix_id}/reward-estimation -``` - -Query Response: - -``` - "estimation": { - "total_node_reward": "942035.916721770541325331", - "operator": "161666.263307386408152071", - "delegates": "780369.65341438413317326", - "operating_cost": "54444.444444444444444443" - }, -``` - -> 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_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_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. - -## Ports -All mix node-specific port configuration can be found in `$HOME/.nym/mixnodes//config/config.toml`. If you do edit any port configs, remember to restart your mix node. - -### Mix node port reference -| Default port | Use | -| ------------ | ------------------------- | -| `1789` | Listen for mixnet traffic | -| `1790` | Listen for VerLoc traffic | -| `8000` | Metrics http API endpoint | diff --git a/documentation/docs/src/nodes/mixnode.md b/documentation/docs/src/nodes/mixnode.md new file mode 100644 index 0000000000..b093c6f0b4 --- /dev/null +++ b/documentation/docs/src/nodes/mixnode.md @@ -0,0 +1,17 @@ +# Mix Nodes + +> The mix node setup and maintenance guide has migrated to the [Operator Guides book](https://nymtech.net/developers/nodes/mix-node-setup.html). + +Mix nodes are the backbone of the mixnet. These are the nodes that perform 'mix mining', otherwise known simply as 'mixing'. + +Mix nodes, after receiving a packet, decrypt its outer 'layer' and hold it for a variable amount of time before passing it to its next destination - either another mix node, or a gateway. In doing so, they 'mix' packets by sending them to their next destination in a different order than they were received. + +Mix nodes are rewarded according to their quality of service, and the probability of their inclusion in the active set (i.e. the nodes that mix traffic for the next epoch) is also affected by this (as well as their delegation-based reputation - see the [Mix node deepdive](#further-reading) below for more on this). + +## (Coming soon) Mixing: a Step-by-Step Breakdown + +## Further reading +* [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) section 4 +* [Nym Blog: Mix node deepdive](https://blog.nymtech.net/nym-mixnodes-deep-dive-d2b91917f097) +* [Mixnet Traffic Flow overview](../architecture/traffic-flow.md) +* [Reward Sharing for Mixnets](https://nymtech.net/nym-cryptoecon-paper.pdf) \ No newline at end of file diff --git a/documentation/docs/src/nodes/network-requester.md b/documentation/docs/src/nodes/network-requester.md new file mode 100644 index 0000000000..ac083a3143 --- /dev/null +++ b/documentation/docs/src/nodes/network-requester.md @@ -0,0 +1,22 @@ +# Network Requester + +> The network requester setup and maintenance guide has moved to the [Operator Guides book](https://nymtech.net/developers/nodes/network-requester-setup.html). + +Network requesters are the first instance of the catch-all term 'service', or 'service providers'. In essence, think of services as being the part of the mixnet infrastructure that let you _do_ something, such as access emails, messaging service backends, or blockchains via the mixnet. + +## Domain filtering +Network requesters, in essence, act as a form of proxy (somewhat analagous to a Tor exit node). 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 whitelist for outbound requests. + +Any request to a URL which is not on this local list (modified by the node operator) or [Nym's default list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) will be blocked. + +This default whitelist is useful for knowing that the majority of Network requesters are able to support certain apps 'out of the box', and the local whitelist allows operators to include their own whitelisted domains. + +> Substantial changes are on the horizon concerning how Network Requesters manage incoming requests - if you are an operator and have experience running software such as Tor exit nodes or p2p nodes get in touch via our [Matrix server](https://matrix.to/#/#dev:nymtech.chat). + +## (Coming soon) Consuming credentials for anonymous service payment + +## Further reading +* [Nym Blog: Network Requester deepdive](https://blog.nymtech.net/tech-deepdive-network-requesters-e5359a6cc31c) +* [Nym Blog: Choose Your Character](https://blog.nymtech.net/choose-your-character-an-overview-of-nym-network-actors-19e6a9808540) \ No newline at end of file diff --git a/documentation/docs/src/nodes/overview.md b/documentation/docs/src/nodes/overview.md new file mode 100644 index 0000000000..497a347365 --- /dev/null +++ b/documentation/docs/src/nodes/overview.md @@ -0,0 +1,9 @@ +# Node Types + +> This section was previously the node setup guides. These have been migrated to their own [**Operator Guides**](https://nymtech.net/operators) book. +> +> For setup and maintenance guides, go to the Operators book linked above. +> +> This section is a little spartan for the moment, but we will be adding detailed information about how exactly each node functions, as well as references to any literature and technical specs in the near future. + +This section contains information on the different node types of the mixnet and blockchain. \ No newline at end of file diff --git a/documentation/docs/src/nodes/setup-guides.md b/documentation/docs/src/nodes/setup-guides.md deleted file mode 100644 index 02a23ebff6..0000000000 --- a/documentation/docs/src/nodes/setup-guides.md +++ /dev/null @@ -1,8 +0,0 @@ -# Node Setup Guides - -This section contains setup guides for the following node types: -* [Mix node](./mix-node-setup.md) -* [Gateway](./gateway-setup.md) -* [Network Requester](./network-requester-setup.md) -* [Validator](./validator-setup.md) - diff --git a/documentation/docs/src/nodes/validator.md b/documentation/docs/src/nodes/validator.md new file mode 100644 index 0000000000..855cf21700 --- /dev/null +++ b/documentation/docs/src/nodes/validator.md @@ -0,0 +1,12 @@ +# Validators + +> The validator setup and maintenance guide has moved to the [Operator Guides book](https://nymtech.net/operators/nodes/validator-setup.html). + +Validators secure the Nyx blockchain via Proof of Stake consensus. The Nyx blockchain records the ledger of `NYM` transactions and executes the smart contracts for distributing `NYM` rewards. The Nyx validators are run via the `nyxd` binary ([codebase](https://github.com/nymtech/nyxd)), maintaining a CosmWasm- and IBC-enabled blockchain. + +The blockchain plays a supporting but fundamental role in the mixnet: the `NYM` token used to incentivise node operators is one of two native tokens of the chain, and the chain is where the [Mixnet](../nyx/mixnet-contract.md) and [Vesting](../nyx/vesting-contract.md) smart contracts are deployed. + +## Further Reading +* Detailed info on Nyx Validators and token flow can be found in [Nym Reward Sharing for Mixnets document](https://nymtech.net/nym-cryptoecon-paper.pdf) in section 2.3 and 2.4. +* Our [quarterly update](https://blog.nymtech.net/quarterly-token-economic-parameter-update-b2862948710f) on token economics from July 2023. +* [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) section 3.1 \ No newline at end of file diff --git a/documentation/docs/src/not-found.md b/documentation/docs/src/not-found.md new file mode 100644 index 0000000000..af87d6dd59 --- /dev/null +++ b/documentation/docs/src/not-found.md @@ -0,0 +1,11 @@ +# Page Not Found + +You seem to have followed a link to a page that no longer exists. Our documentation is changing and growing over time, so this page has most likely been moved somewhere else. + +For node setup guides, see the [Operator Guides book](https://nymtech.net/operators). + +For developer tutorials, app guides, and demo and community apps, see the [Developer Portal](https://nymtech.net/developers). + +If you are looking for information on network architecture traffic flow, clients, the SDKs, or interacting with the Nyx blockchain, check the sitemap on the left hand side of the screen. + +If you still can't find what you're looking for get in touch with us via [Matrix](https://matrix.to/#/#nym-community:nymtech.chat). \ No newline at end of file diff --git a/documentation/docs/src/nyx/ledger-live.md b/documentation/docs/src/nyx/ledger-live.md index f7559b6fd2..031834fa24 100644 --- a/documentation/docs/src/nyx/ledger-live.md +++ b/documentation/docs/src/nyx/ledger-live.md @@ -4,7 +4,7 @@ Use the following instructions to interact with the Nyx blockchain - either with ## Prerequisites * Download and install [Ledger Live](https://www.ledger.com/ledger-live). -* Compile the `nyxd` binary as per the instructions [here](../nodes/validator-setup.md#building-the-nym-validator). Stop after you can successfully run `nyxd` and get the helptext in your console output. +* Compile the `nyxd` binary as per the instructions [here](https://nymtech.net/operators/nodes/validator-setup.md#building-the-nym-validator). Stop after you can successfully run `nyxd` and get the helptext in your console output. ## Prepare your Ledger App * Plug in your Ledger device diff --git a/documentation/docs/src/nyx/rpc-node.md b/documentation/docs/src/nyx/rpc-node.md index af76f1f927..dc48d68409 100644 --- a/documentation/docs/src/nyx/rpc-node.md +++ b/documentation/docs/src/nyx/rpc-node.md @@ -4,6 +4,6 @@ RPC Nodes (which might otherwise be referred to as 'Lite Nodes' or just 'Full No You may want to set up an RPC Node for querying the blockchain, or in order to have an endpoint that your app can use to send transactions. -In order to set up an RPC Node, simply follow the instructions to set up a [Validator](../nodes/validator-setup.md), but **exclude the `nyxd tx staking create-validator` command**. +In order to set up an RPC Node, simply follow the instructions to set up a [Validator](https://nymtech.net/operators/nodes/validator-setup.md), but **exclude the `nyxd tx staking create-validator` command**. If you want to fast-sync your node, check out the Polkachu snapshot and their other [resources](https://polkachu.com/seeds/nym). \ No newline at end of file diff --git a/documentation/docs/src/sdk/rust.md b/documentation/docs/src/sdk/rust.md index 035d8b37b5..70015751e4 100644 --- a/documentation/docs/src/sdk/rust.md +++ b/documentation/docs/src/sdk/rust.md @@ -77,12 +77,12 @@ In order to reply to an incoming message using SURBs, you can construct a `recip ``` ### Importing and using a custom network topology -If you want to send traffic through a sub-set of nodes (for instance, ones you control, or a small test setup) when developing, debugging, or peforming research, you will need to import these nodes as a custom network topology, instead of grabbing it from the [`Mainnet Nym-API`](https://validator.nymtech.net/api/swagger/index.html) (`examples/custom_topology_provider.rs`). +If you want to send traffic through a sub-set of nodes (for instance, ones you control, or a small test setup) when developing, debugging, or performing research, you will need to import these nodes as a custom network topology, instead of grabbing it from the [`Mainnet Nym-API`](https://validator.nymtech.net/api/swagger/index.html) (`examples/custom_topology_provider.rs`). There are two ways to do this: #### Import a custom Nym API endpoint -If you are also running a Validator and Nym API for your network, you can specify that endpoint as such and interact with it as clients ususally do (under the hood): +If you are also running a Validator and Nym API for your network, you can specify that endpoint as such and interact with it as clients usually do (under the hood): ```rust,noplayground {{#include ../../../../sdk/rust/nym-sdk/examples/custom_topology_provider.rs}} diff --git a/documentation/docs/src/sdk/typescript.md b/documentation/docs/src/sdk/typescript.md index eed1167692..5ce939e858 100644 --- a/documentation/docs/src/sdk/typescript.md +++ b/documentation/docs/src/sdk/typescript.md @@ -47,7 +47,7 @@ There are multiple example projects in [`nym/sdk/typescript/examples/`](https:// The best place to start if you just want to quickly get a basic frontend up and running with which to experiment is `examples/plain-html`: ```typescript -{{#include ../../../../sdk/typescript/examples/plain-html/src/index.ts}} +{{#include ../../../../sdk/typescript/examples/chat-app/plain-html/src/index.ts}} ``` As you can see, all that is required to create an ephemeral keypair and connect to the mixnet is creating a client and then subscribing to the mixnet events coming down the websocket, and adding logic to deal with them. diff --git a/documentation/docs/src/tools/nym-cli.md b/documentation/docs/src/tools/nym-cli.md index f516190002..b6ed36bfa7 100644 --- a/documentation/docs/src/tools/nym-cli.md +++ b/documentation/docs/src/tools/nym-cli.md @@ -4,14 +4,14 @@ This is a CLI tool for interacting with: * the Nyx blockchain (account management, querying the chain state, etc) -* the smart contracts deployed on Nyx (bonding and unbonding mixnodes, collecting rewards, etc) +* the smart contracts deployed on Nyx (bonding and un-bonding mixnodes, collecting rewards, etc) It provides a convenient wrapper around the `nymd` client, and has similar functionality to the `nyxd` binary for querying the chain or executing smart contract methods. ## Building The `nym-cli` binary can be built by running `cargo build --release` in the `nym/tools/nym-cli` directory. -### Useage +### Usage You can see all available commands with: ``` @@ -225,9 +225,9 @@ Bonding a mix node is a process that takes a few steps due to the need to sign a ./nym-cli --mnemonic mixnet operators gateway bond --amount 100000000 --mix-port 1789 --version "1.1.13" --host "85.163.111.99" --identity-key "B6pWscxYb8sPAdKTci8zPy5AgMzn5Zx8KpWwQNCyUSU7" --location "nym-town" --sphinx-key "o6MmKHzRewpNzVwaV37ZX9G3BfK4AmfYvsQfyoyAFRk" --signature "2TujBZfer8r5QM639Yb8coD9xH6f5eXzjAT5dD7wMom9fH8D1u36d7UpPdVaaZrWsCynmYpobwMWqiMKr5kM6CprD" ``` -### Unbond a node +### Un-bond a node -Unbond a mix node or gateway. +Un-bond a mix node or gateway. ``` ./nym-cli mixnet operators gateway unbound --mnemonic ``` @@ -270,7 +270,7 @@ Delegate to a mix node. ./nym-cli mixnet delegators delegate --amount –mix-id --mnemonic ``` -### Undelegate Stake +### Un-delegate Stake Remove stake from a mix node. ``` @@ -321,10 +321,10 @@ Query for vesting schedule in the [vesting smart contract](../nyx/vesting-contra ## Staking on someone's behalf (for custodians) -There is a limitation the the staking address can only perform the following actions (and are visible via the Nym Wallet: +There is a limitation the staking address can only perform the following actions (and are visible via the Nym Wallet: -- Bond on the gaƒteway's or mix node's behalf. -- Delegate or Undelegate (to a mix node in order to begin receiving rewards) +- Bond on the gateway's or mix node's behalf. +- Delegate or Un-delegate (to a mix node in order to begin receiving rewards) - Claiming the rewards on the account ```admonish note title="" @@ -333,7 +333,7 @@ The staking address has no ability to withdraw any coins from the parent's accou The staking address must maintain the same level of security as the parent mnemonic; while the parent mnemonic's delegations and bonding events will be visible to the parent owner, the staking address will be the only account capable of undoing the bonding and delegating from the mix nodes or gateway. -Query for staking on behlaf of someone else +Query for staking on behalf of someone else ``` ./nym-cli --mnemonic mixnet delegators delegate --mix-id --identity-key --amount ``` diff --git a/documentation/docs/src/wallet/cli-wallet.md b/documentation/docs/src/wallet/cli-wallet.md index e4d2468068..36670190a3 100644 --- a/documentation/docs/src/wallet/cli-wallet.md +++ b/documentation/docs/src/wallet/cli-wallet.md @@ -1,8 +1,8 @@ # CLI Wallet -If you have already read our validator setup and maintenance [documentation](../nodes/validator-setup.md) you will have seen that we compile and use the `nyxd` binary primarily for our validators. This binary can however be used for many other tasks, such as creating and using keypairs for wallets, or automated setups that require the signing and broadcasting of transactions. +If you have already read our validator setup and maintenance [documentation](../nodes/validator.md) you will have seen that we compile and use the `nyxd` binary primarily for our validators. This binary can however be used for many other tasks, such as creating and using keypairs for wallets, or automated setups that require the signing and broadcasting of transactions. ### Using `nyxd` binary as a CLI wallet -You can use the `nyxd` as a minimal CLI wallet if you want to set up an account (or multiple accounts). Just compile the binary as per the documentation, **stopping after** the [building your validator](../nodes/validator-setup.md#building-your-validator) step is complete. You can then run `nyxd keys --help` to see how you can set up and store different keypairs with which to interact with the Nyx blockchain. +You can use the `nyxd` as a minimal CLI wallet if you want to set up an account (or multiple accounts). Just compile the binary as per the documentation, **stopping after** the [building your validator](../nodes/validator.md) step is complete. You can then run `nyxd keys --help` to see how you can set up and store different keypairs with which to interact with the Nyx blockchain. - +For more on interacting with the chain, see the [Interacting with Nyx Chain and Smart Contracts](../nyx/interacting-with-chain.md) page. diff --git a/documentation/operators/.gitignore b/documentation/operators/.gitignore new file mode 100644 index 0000000000..7585238efe --- /dev/null +++ b/documentation/operators/.gitignore @@ -0,0 +1 @@ +book diff --git a/documentation/operators/book.toml b/documentation/operators/book.toml new file mode 100644 index 0000000000..be7233c069 --- /dev/null +++ b/documentation/operators/book.toml @@ -0,0 +1,125 @@ +[book] +title = "Nym Operators Guide" +authors = ["Max Hampshire", "serinko"] +description = "Guides to setup Nym infrastructure" +language = "en" +multilingual = false # for the moment - ideally work on chinese, brazillian, spanish next +src = "src" + +################# +# PREPROCESSORS # +################# + +# Note if changing these fields: +# - make sure you aren't running `mdbook serve` +# - change value of `turn-off = true` to `false`. This is set to `true` usually to avoid a known constant-reload bug with `mdbook serve` and this preprocessor +# - delete `./theme/` dir +# - change the preprocessor values that you want to edit +# - run `mdbook build`: this will rebuild the `./theme` directory +# - change value of `turn-off` back to `true` +[preprocessor.theme] +pagetoc = true +# some variables related (defined in theme/css/variables.css) +pagetoc-width = "13%" +content-max-width = "70%" # 82 +pagetoc-fontsize = "14.5px" +sidebar-width = "300px" +menu-bar-height = "40px" # memu-bar = the bar on the top +page-padding = "20px" +mobile-content-max-width = "98%" + +# layout +content-padding = "0 10px" +content-main-margin-left = "10%" +content-main-margin-right = "10%" +nav-chapters-max-width = "auto" +nav-chapters-min-width = "auto" +chapter-line-height = "2em" +section-line-height = "1.5em" + +# if true, never read and touch the files in theme dir: this is used to stop the looping reload issue referred to in the readme +turn-off = false + +[preprocessor.admonish] +command = "mdbook-admonish" +assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` + +# variables preprocessor: import variables into files +# https://gitlab.com/tglman/mdbook-variables/ +[preprocessor.variables.variables] +minimum_rust_version = "1.66" +# vars for links: TODO think on how to streamline updating +platform_release_version = "v1.1.27" +wallet_release_version = "v1.2.7" + + +[preprocessor.last-changed] +command = "mdbook-last-changed" +renderer = ["html"] + + +# used for grabbing output of binary commands for automation +# https://github.com/FauconFan/mdbook-cmdrun +[preprocessor.cmdrun] + +# more pre-processor plugins to look into from https://github.com/rust-lang/mdBook/wiki/Third-party-plugins & https://lib.rs/keywords/mdbook-preprocessor +# mdbook-i18n + +######### +# BUILD # +######### + +[build] +build-dir = "book" # the directory where the output is placed +create-missing = true # whether or not to create missing pages +use-default-preprocessors = true # use the default preprocessors +extra-watch-dirs = [] # directories to watch for triggering builds + +########## +# OUTPUT # +########## + +[output.html] +theme = "theme" # this is the default if not explicitly set +default-theme = "coal" +preferred-dark-theme = "coal" +curly-quotes = true +# mathjax-support = false # useful if we want to pull equations in +copy-fonts = true +no-section-label = false +additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css","./custom.css"] +additional-js = ["theme/pagetoc.js"] +git-repository-url = "https://github.com/nymtech/nym" +git-repository-icon = "fa-github" +# edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" +site-url = "/operators/" +# cname = "nymtech.net" +input-404 = "not-found.md" + +[output.html.fold] +enable = true # whether or not to enable section folding +level = 0 # the depth to start folding + +# controlling rust sample code blocks +[output.html.playground] +editable = false # allows editing the source code +copyable = true # include the copy button for copying code snippets +copy-js = true # includes the JavaScript for the code editor +line-numbers = false # displays line numbers for editable code +runnable = true # displays a run button for rust code + +# options for the built in text search +[output.html.search] +enable = true # enables the search feature +limit-results = 30 # maximum number of search results +teaser-word-count = 30 # number of words used for a search result teaser +use-boolean-and = true # multiple search terms must all match +boost-title = 2 # ranking boost factor for matches in headers +boost-hierarchy = 1 # ranking boost factor for matches in page names +boost-paragraph = 1 # ranking boost factor for matches in text +expand = true # partial words will match longer terms +heading-split-level = 3 # link results to heading levels +copy-js = true # include Javascript code for search + +[output.linkcheck] +warning-policy = "warn" diff --git a/documentation/operators/custom.css b/documentation/operators/custom.css new file mode 100644 index 0000000000..855f5eef5f --- /dev/null +++ b/documentation/operators/custom.css @@ -0,0 +1,37 @@ +:root { + --mono-font: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace; +} +.coal { + --bg: #121726; + --fg: #f2f2f2; + + --sidebar-bg: #121726; + --sidebar-fg: #f2f2f2; + --sidebar-active: #fb6e4e; + + --icons: #f2f2f2; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +.light { + --bg: #f4f6f8; + --fg: #121726; + + --sidebar-bg: #f4f6f8; + --sidebar-fg: #121726; + --sidebar-active: #fb6e4e; + + --icons: #121726; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +footer { + font-size: 0.8em; + text-align: center; + border-top: 1px solid black; + padding: 5px 0; +} diff --git a/documentation/dev-portal/last-changed.css b/documentation/operators/last-changed.css similarity index 100% rename from documentation/dev-portal/last-changed.css rename to documentation/operators/last-changed.css diff --git a/documentation/operators/mdbook-admonish.css b/documentation/operators/mdbook-admonish.css new file mode 100644 index 0000000000..5e360387df --- /dev/null +++ b/documentation/operators/mdbook-admonish.css @@ -0,0 +1,352 @@ +@charset "UTF-8"; +:root { + --md-admonition-icon--note: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--abstract: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--info: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--tip: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--success: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--question: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--warning: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--failure: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--danger: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--bug: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--example: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--quote: + url("data:image/svg+xml;charset=utf-8,"); + --md-details-icon: + url("data:image/svg+xml;charset=utf-8,"); +} + +:is(.admonition) { + display: flow-root; + margin: 1.5625em 0; + padding: 0 1.2rem; + color: var(--fg); + page-break-inside: avoid; + background-color: var(--bg); + border: 0 solid black; + border-inline-start-width: 0.4rem; + border-radius: 0.2rem; + box-shadow: 0 0.2rem 1rem rgba(0, 0, 0, 0.05), 0 0 0.1rem rgba(0, 0, 0, 0.1); +} +@media print { + :is(.admonition) { + box-shadow: none; + } +} +:is(.admonition) > * { + box-sizing: border-box; +} +:is(.admonition) :is(.admonition) { + margin-top: 1em; + margin-bottom: 1em; +} +:is(.admonition) > .tabbed-set:only-child { + margin-top: 0; +} +html :is(.admonition) > :last-child { + margin-bottom: 1.2rem; +} + +a.admonition-anchor-link { + display: none; + position: absolute; + left: -1.2rem; + padding-right: 1rem; +} +a.admonition-anchor-link:link, a.admonition-anchor-link:visited { + color: var(--fg); +} +a.admonition-anchor-link:link:hover, a.admonition-anchor-link:visited:hover { + text-decoration: none; +} +a.admonition-anchor-link::before { + content: "§"; +} + +:is(.admonition-title, summary) { + position: relative; + margin-block: 0; + margin-inline: -1.6rem -1.2rem; + padding-block: 0.8rem; + padding-inline: 4.4rem 1.2rem; + font-weight: 700; + background-color: rgba(68, 138, 255, 0.1); + display: flex; +} +:is(.admonition-title, summary) p { + margin: 0; +} +html :is(.admonition-title, summary):last-child { + margin-bottom: 0; +} +:is(.admonition-title, summary)::before { + position: absolute; + top: 0.625em; + inset-inline-start: 1.6rem; + width: 2rem; + height: 2rem; + background-color: #448aff; + mask-image: url('data:image/svg+xml;charset=utf-8,'); + -webkit-mask-image: url('data:image/svg+xml;charset=utf-8,'); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-size: contain; + content: ""; +} +:is(.admonition-title, summary):hover a.admonition-anchor-link { + display: initial; +} + +details.admonition > summary.admonition-title::after { + position: absolute; + top: 0.625em; + inset-inline-end: 1.6rem; + height: 2rem; + width: 2rem; + background-color: currentcolor; + mask-image: var(--md-details-icon); + -webkit-mask-image: var(--md-details-icon); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-size: contain; + content: ""; + transform: rotate(0deg); + transition: transform 0.25s; +} +details[open].admonition > summary.admonition-title::after { + transform: rotate(90deg); +} + +:is(.admonition):is(.note) { + border-color: #448aff; +} + +:is(.note) > :is(.admonition-title, summary) { + background-color: rgba(68, 138, 255, 0.1); +} +:is(.note) > :is(.admonition-title, summary)::before { + background-color: #448aff; + mask-image: var(--md-admonition-icon--note); + -webkit-mask-image: var(--md-admonition-icon--note); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.abstract, .summary, .tldr) { + border-color: #00b0ff; +} + +:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary) { + background-color: rgba(0, 176, 255, 0.1); +} +:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::before { + background-color: #00b0ff; + mask-image: var(--md-admonition-icon--abstract); + -webkit-mask-image: var(--md-admonition-icon--abstract); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.info, .todo) { + border-color: #00b8d4; +} + +:is(.info, .todo) > :is(.admonition-title, summary) { + background-color: rgba(0, 184, 212, 0.1); +} +:is(.info, .todo) > :is(.admonition-title, summary)::before { + background-color: #00b8d4; + mask-image: var(--md-admonition-icon--info); + -webkit-mask-image: var(--md-admonition-icon--info); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.tip, .hint, .important) { + border-color: #00bfa5; +} + +:is(.tip, .hint, .important) > :is(.admonition-title, summary) { + background-color: rgba(0, 191, 165, 0.1); +} +:is(.tip, .hint, .important) > :is(.admonition-title, summary)::before { + background-color: #00bfa5; + mask-image: var(--md-admonition-icon--tip); + -webkit-mask-image: var(--md-admonition-icon--tip); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.success, .check, .done) { + border-color: #00c853; +} + +:is(.success, .check, .done) > :is(.admonition-title, summary) { + background-color: rgba(0, 200, 83, 0.1); +} +:is(.success, .check, .done) > :is(.admonition-title, summary)::before { + background-color: #00c853; + mask-image: var(--md-admonition-icon--success); + -webkit-mask-image: var(--md-admonition-icon--success); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.question, .help, .faq) { + border-color: #64dd17; +} + +:is(.question, .help, .faq) > :is(.admonition-title, summary) { + background-color: rgba(100, 221, 23, 0.1); +} +:is(.question, .help, .faq) > :is(.admonition-title, summary)::before { + background-color: #64dd17; + mask-image: var(--md-admonition-icon--question); + -webkit-mask-image: var(--md-admonition-icon--question); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.warning, .caution, .attention) { + border-color: #ff9100; +} + +:is(.warning, .caution, .attention) > :is(.admonition-title, summary) { + background-color: rgba(255, 145, 0, 0.1); +} +:is(.warning, .caution, .attention) > :is(.admonition-title, summary)::before { + background-color: #ff9100; + mask-image: var(--md-admonition-icon--warning); + -webkit-mask-image: var(--md-admonition-icon--warning); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.failure, .fail, .missing) { + border-color: #ff5252; +} + +:is(.failure, .fail, .missing) > :is(.admonition-title, summary) { + background-color: rgba(255, 82, 82, 0.1); +} +:is(.failure, .fail, .missing) > :is(.admonition-title, summary)::before { + background-color: #ff5252; + mask-image: var(--md-admonition-icon--failure); + -webkit-mask-image: var(--md-admonition-icon--failure); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.danger, .error) { + border-color: #ff1744; +} + +:is(.danger, .error) > :is(.admonition-title, summary) { + background-color: rgba(255, 23, 68, 0.1); +} +:is(.danger, .error) > :is(.admonition-title, summary)::before { + background-color: #ff1744; + mask-image: var(--md-admonition-icon--danger); + -webkit-mask-image: var(--md-admonition-icon--danger); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.bug) { + border-color: #f50057; +} + +:is(.bug) > :is(.admonition-title, summary) { + background-color: rgba(245, 0, 87, 0.1); +} +:is(.bug) > :is(.admonition-title, summary)::before { + background-color: #f50057; + mask-image: var(--md-admonition-icon--bug); + -webkit-mask-image: var(--md-admonition-icon--bug); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.example) { + border-color: #7c4dff; +} + +:is(.example) > :is(.admonition-title, summary) { + background-color: rgba(124, 77, 255, 0.1); +} +:is(.example) > :is(.admonition-title, summary)::before { + background-color: #7c4dff; + mask-image: var(--md-admonition-icon--example); + -webkit-mask-image: var(--md-admonition-icon--example); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.quote, .cite) { + border-color: #9e9e9e; +} + +:is(.quote, .cite) > :is(.admonition-title, summary) { + background-color: rgba(158, 158, 158, 0.1); +} +:is(.quote, .cite) > :is(.admonition-title, summary)::before { + background-color: #9e9e9e; + mask-image: var(--md-admonition-icon--quote); + -webkit-mask-image: var(--md-admonition-icon--quote); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +.navy :is(.admonition) { + background-color: var(--sidebar-bg); +} + +.ayu :is(.admonition), .coal :is(.admonition) { + background-color: var(--theme-hover); +} + +.rust :is(.admonition) { + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.rust .admonition-anchor-link:link, .rust .admonition-anchor-link:visited { + color: var(--sidebar-fg); +} diff --git a/documentation/operators/src/SUMMARY.md b/documentation/operators/src/SUMMARY.md new file mode 100644 index 0000000000..82345c6f49 --- /dev/null +++ b/documentation/operators/src/SUMMARY.md @@ -0,0 +1,27 @@ +# Summary + +- [Introduction](introduction.md) + +# Binaries +- [Pre-built Binaries](./binaries/pre-built-binaries.md) +- [Building from Source](./binaries/building-nym.md) +- [Binary Initialisation and Configuration](./binaries/init-and-config.md) + + +# Operators Guides + +- [Mixnet Nodes Setup](./nodes/setup-guides.md) + - [Preliminary Steps](./preliminary-steps.md) + - [Mix Node](./nodes/mix-node-setup.md) + - [Gateway](./nodes/gateway-setup.md) + - [Network Requester](./nodes/network-requester-setup.md) +- [Nyx Validator Setup](./nodes/validator-setup.md) +- [Maintenance](./nodes/maintenance.md) +- [Troubleshooting](./nodes/troubleshooting.md) +- [FAQ](./faq.md) + +--- +# Misc. +- [Code of Conduct](coc.md) +- [Licensing](licensing.md) +--- diff --git a/documentation/operators/src/binaries/building-nym.md b/documentation/operators/src/binaries/building-nym.md new file mode 100644 index 0000000000..8610fcb63a --- /dev/null +++ b/documentation/operators/src/binaries/building-nym.md @@ -0,0 +1,71 @@ +# Building from Source + +> Nym runs on Mac OS X, Linux, and Windows. All nodes **except the Desktop Wallet and NymConnect** on Windows should be considered experimental - it works fine if you're an app developer but isn't recommended for running nodes. + +## Building Nym +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. + +> This page details how to build the main Nym platform code. **If you want to build and run a validator, [go here](../nodes/validator-setup.md) instead.** + +## Prerequisites +- Debian/Ubuntu: `pkg-config`, `build-essential`, `libssl-dev`, `curl`, `jq`, `git` + +``` +apt install pkg-config build-essential libssl-dev curl jq git +``` + +- Arch/Manjaro: `base-devel` + +``` +pacman -S base-devel +``` + +- Mac OS X: `pkg-config` , `brew`, `openss1`, `protobuf`, `curl`, `git` +Running the following the script installs Homebrew and the above dependencies: + +``` +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +- `Rust & cargo >= {{minimum_rust_version}}` + +We recommend using the [Rust shell script installer](https://www.rust-lang.org/tools/install). Installing cargo from your package manager (e.g. `apt`) is not recommended as the packaged versions are usually too old. + +If you really don't want to use the shell script installer, the [Rust installation docs](https://forge.rust-lang.org/infra/other-installation-methods.html) contain instructions for many platforms. + +## Download and build Nym binaries +The following commands will compile binaries into the `nym/target/release` directory: + +``` +rustup update +git clone https://github.com/nymtech/nym.git +cd nym + +git reset --hard # in case you made any changes on your branch +git pull # in case you've checked it out before + +git checkout release/{{platform_release_version}} # checkout to the latest release branch: `develop` will most likely be incompatible with deployed public networks + +cargo build --release # build your binaries with **mainnet** configuration +NETWORK=sandbox cargo build --release # build your binaries with **sandbox** configuration +``` + +Quite a bit of stuff gets built. The key working parts are: + +* [mix node](../nodes/mix-node-setup.md): `nym-mixnode` +* [gateway node](../nodes/gateway-setup.md): `nym-gateway` +* [websocket client](https://nymtech.net/docs/clients/websocket-client.html): `nym-client` +* [socks5 client](https://nymtech.net/docs/clients/socks5-client.html): `nym-socks5-client` +* [webassembly client](https://nymtech.net/docs/clients/webassembly-client.html): `webassembly-client` +* [network requester](../nodes/network-requester-setup.md): `nym-network-requester` +* [nym-cli tool](https://nymtech.net/docs/tools/nym-cli.html): `nym-cli` + +The repository also contains Typescript applications which aren't built in this process. These can be built by following the instructions on their respective docs pages. +* [Nym Wallet](https://nymtech.net/docs/wallet/desktop-wallet.html) +* [Nym Connect](https://nymtech.net/developers/quickstart/nymconnect-gui.html) +* [Network Explorer UI](https://nymtech.net/docs/explorers/mixnet-explorer.html) + +> You cannot build from GitHub's .zip or .tar.gz archive files on the releases page - the Nym build scripts automatically include the current git commit hash in the built binary during compilation, so the build will fail if you use the archive code (which isn't a Git repository). Check the code out from github using `git clone` instead. diff --git a/documentation/operators/src/binaries/init-and-config.md b/documentation/operators/src/binaries/init-and-config.md new file mode 100644 index 0000000000..e749662b8e --- /dev/null +++ b/documentation/operators/src/binaries/init-and-config.md @@ -0,0 +1,13 @@ +# Binary Initialisation and Configuration + +All Nym binaries must first be initialised with `init` before being `run`. + +The `init` command is usually where you pass flags specifying configuration arguments such as the gateway you wish to communicate with, the ports you wish your binary to listen on, etc. + +The `init` command will also create the necessary keypairs and configuration files at `~/.nym///` if these files do not already exist. **It will not overwrite existing keypairs if they are present.** + +You can reconfigure your binaries at any time by editing the config file located at `~/.nym///config/config.toml` and restarting the binary process. + +Once you have run `init`, you can start your binary with the `run` command, usually only accompanied by the `id` of the binary that you specified. + +This `id` is **never** transmitted over the network, and is used to select which local config and key files to use for startup. diff --git a/documentation/operators/src/binaries/pre-built-binaries.md b/documentation/operators/src/binaries/pre-built-binaries.md new file mode 100644 index 0000000000..269c26276c --- /dev/null +++ b/documentation/operators/src/binaries/pre-built-binaries.md @@ -0,0 +1,6 @@ +# Pre-built Binaries + +The [Github releases page](https://github.com/nymtech/nym/releases) has pre-built binaries which should work on Ubuntu 20.04 and other Debian-based systems, but at this stage cannot be guaranteed to work everywhere. + +If the pre-built binaries don't work or are unavailable for your system, you will need to build the platform yourself. + diff --git a/documentation/operators/src/binaries/version-compatiblity.md b/documentation/operators/src/binaries/version-compatiblity.md new file mode 100644 index 0000000000..67e8a71ce6 --- /dev/null +++ b/documentation/operators/src/binaries/version-compatiblity.md @@ -0,0 +1,38 @@ +# Version Compatibility Table + +There are numerous components to Nym which are released independently of one another aside from when breaking changes occur in the [core platform code](https://github.com/nymtech/nym/) - mix nodes, gateways, network requesters, and clients. + +Whilst in general it recommended to be running the most recent version of any software, if you cannot do that for whatever reason this table will tell you which versions of different components are mutually compatible with which platform code releases. + + +| Core Platform | SDK | Wallet | NymConnect | Network Explorer | Mixnet contract | Vesting contract | +| ---------------- | ------------- | --------------- | --------------- | ---------------- | --------------- | ---------------- | +| 1.1.13 - 1.1.14* | 1.1.7 | 1.1.12 - 1.1.13 | 1.1.12 - 1.1.13 | 1.1.2 | 1.2.0 - 1.3.0 | 1.2.0 - 1.3.0 | +| 1.1.1 - 1.1.12 | 1.1.4 - 1.1.7 | 1.1.0 - 1.1.12 | 1.1.1 - 1.1.12 | 1.1.0 - 1.1.2 | 1.1.0 - 1.1.3 | 1.1.0 - 1.1.3 | +| 1.1.0 - 1.1.1 | 1.1.4 | 1.1.0 | 1.1.0 - 1.1.1 | 1.1.0 | 1.1.0 | 1.1.0 | +| 1.1.0 | x | 1.1.0 | 1.1.0 | 1.1.0 | 1.1.0 | 1.1.0 | + +`*` the `nym-mixnode` binary is currently one point ahead of the main platform release version + +> There are seperate changelogs for [`NymConnect`](https://github.com/nymtech/nym/blob/release/{{platform_release_version}}/nym-connect/CHANGELOG.md) and the [`Desktop Wallet`](https://github.com/nymtech/nym/blob/release/{{platform_release_version}}/nym-wallet/CHANGELOG.md). The changelog referenced below is for the core platform code. + +| Platform release changelog | +| ---------------------------------------------------------------------------------------- | +| 1.1.14 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.14/CHANGELOG.md)) | +| 1.1.13 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.13/CHANGELOG.md)) | +| 1.1.12 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.12/CHANGELOG.md)) | +| 1.1.11 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.11/CHANGELOG.md)) | +| 1.1.10 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.10/CHANGELOG.md)) | +| 1.1.9 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.9/CHANGELOG.md)) | +| 1.1.8 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.8/CHANGELOG.md)) | +| 1.1.7 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.7/CHANGELOG.md)) | +| 1.1.6 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.6/CHANGELOG.md)) | +| 1.1.5 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.5/CHANGELOG.md)) | +| 1.1.4 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.4/CHANGELOG.md)) | +| 1.1.3 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.3/CHANGELOG.md)) | +| 1.1.2 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.2/CHANGELOG.md)) | +| 1.1.1 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.1/CHANGELOG.md)) | +| 1.1.0 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.0/CHANGELOG.md)) | +| 1.0.2 ([CHANGELOG](https://github.com/nymtech/nym/blob/nym-binaries-1.0.2/CHANGELOG.md)) | +| 1.0.1 ([CHANGELOG](https://github.com/nymtech/nym/blob/nym-binaries-1.0.1/CHANGELOG.md)) | +| 1.0.0 ([CHANGELOG](https://github.com/nymtech/nym/blob/nym-binaries-1.0.0/CHANGELOG.md)) | diff --git a/documentation/operators/src/coc.md b/documentation/operators/src/coc.md new file mode 100644 index 0000000000..f8771a73bf --- /dev/null +++ b/documentation/operators/src/coc.md @@ -0,0 +1,17 @@ +# Code of Conduct + +We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. + +Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all. + +Please be kind and courteous. There’s no need to be mean or rude. + +Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. + +Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. + +We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behaviour. We interpret the term “harassment” as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behaviour that excludes people in socially marginalized groups. + +Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the Rust moderation team immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back. + +Likewise any spamming, trolling, flaming, baiting or other attention-stealing behaviour is not welcome. diff --git a/documentation/operators/src/faq.md b/documentation/operators/src/faq.md new file mode 100644 index 0000000000..9bb2c6dec9 --- /dev/null +++ b/documentation/operators/src/faq.md @@ -0,0 +1,57 @@ +# Frequently Asked Questions + +## Mixnet nodes + +### 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 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. + +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 chose smaller and decentralized VPS providers over the most known ones controlling a majority of the internet. We receive some good feedback on these: Hertzner, DigitalOcean, Linode, Ghandi and Exoscale. Do your own research and share with the community. + + + +### What's the Sphinx packet size? + +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? + +Becauase 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. + +### Are mixnodes whitelisted? + +Nope, anyone can run a mix node. Purely reliant on the node's reputation (self stake + delegations) & routing score. + +## Validators and tokens + +### What's the difference between NYM and uNYM? + +1 NYM = 1 000 000 uNYM + + + +### 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. + +### Why is validator set entry whitelisted? + +We understand that the early days of the Nyx blockchain will face possible vulnerabilities in terms of size - easy to disrupt or halt the chain if a malicious party entered with a large portion of stake. Besides that, there are some legal issues we need to address before we can distribute the validator set in a fully permissions fashion. + +### Why does Nym do many airdrops? + +It is part of ensuring decentralisation - we need to avoid a handful of people having too much control over the token and market. Of course ideally people will stake the tokens and contribute to the project at this stage. We run surveys to better understand what people are doing with their tokens and what usability issues there are for staking. Any feedback is appreciated as it helps us improve all aspects of using the token and participating in the ecosystem. diff --git a/documentation/operators/src/images/wallet-screenshots/bonding.png b/documentation/operators/src/images/wallet-screenshots/bonding.png new file mode 100644 index 0000000000..b5cef3bb89 Binary files /dev/null and b/documentation/operators/src/images/wallet-screenshots/bonding.png differ diff --git a/documentation/operators/src/images/wallet-screenshots/node_settings.png b/documentation/operators/src/images/wallet-screenshots/node_settings.png new file mode 100644 index 0000000000..69568ce6a5 Binary files /dev/null and b/documentation/operators/src/images/wallet-screenshots/node_settings.png differ diff --git a/documentation/operators/src/images/wallet-screenshots/wallet-gateway-sign.png b/documentation/operators/src/images/wallet-screenshots/wallet-gateway-sign.png new file mode 100644 index 0000000000..63a76aaf4d Binary files /dev/null and b/documentation/operators/src/images/wallet-screenshots/wallet-gateway-sign.png differ diff --git a/documentation/operators/src/images/wallet-screenshots/wallet-sign.png b/documentation/operators/src/images/wallet-screenshots/wallet-sign.png new file mode 100644 index 0000000000..a31dddd391 Binary files /dev/null and b/documentation/operators/src/images/wallet-screenshots/wallet-sign.png differ diff --git a/documentation/operators/src/introduction.md b/documentation/operators/src/introduction.md new file mode 100644 index 0000000000..4050fe141c --- /dev/null +++ b/documentation/operators/src/introduction.md @@ -0,0 +1,26 @@ +# 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. + +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/). + +If you want to dive deeper into Nym's architecture, clients, nodes, and SDK examples visit the [technical docs](https://nymtech.net/docs/). + + +## 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) + +**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) + +**Maintenance, troubleshooting and FAQ** +* [Maintenance](./nodes/maintenance.md) +* [Troubleshooting](./nodes/troubleshooting.md) +* [FAQ](./faq.md) + diff --git a/documentation/operators/src/licensing.md b/documentation/operators/src/licensing.md new file mode 100644 index 0000000000..2e8612d4d4 --- /dev/null +++ b/documentation/operators/src/licensing.md @@ -0,0 +1,5 @@ +# Licensing + +Nym is Free Software released under the Apache License V2. + +All of the contributions of the Nym core developers are © Nym Technologies SA. If you are interested in talking to us about other licenses, please get in touch. diff --git a/documentation/operators/src/nodes/gateway-setup.md b/documentation/operators/src/nodes/gateway-setup.md new file mode 100644 index 0000000000..f1cb589756 --- /dev/null +++ b/documentation/operators/src/nodes/gateway-setup.md @@ -0,0 +1,136 @@ +# Gateways + +> The Nym gateway 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. + +## Current version +``` + +``` + +## Preliminary steps + +Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your gateway. + + +## Gateway setup +Now that you have built the codebase, set up your wallet, and have a VPS with the `nym-gateway` binary, you can set up your gateway with the instructions below. + +To begin, move to `/target/release` directory from which you run the node commands: + +``` +cd target/release +``` + +### Viewing command help +You can check that your binaries are properly compiled with: + +``` +./nym-gateway --help +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +You can also check the various arguments required for individual commands with: + +``` +./nym-gateway --help +``` +> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +### Initialising your gateway +To check available configuration options use: + +``` + ./nym-gateway init --help +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +The following command returns a gateway on your current IP with the `id` of `supergateway`: + +``` +./nym-gateway init --id supergateway --host $(curl ifconfig.me) --wallet-address n1eufxdlgt0puwrwptgjfqne8pj4nhy2u5ft62uq +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +The `$(curl 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. + +### Bonding your gateway +#### Via the Desktop wallet +You can bond your gateway via the Desktop wallet. + +1. Open your wallet, and head to the `Bonding` page, then select the node type `Gateway` and input your node details (Location format is , ). 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. + +``` +./nym-mixnode sign --id --contract-msg +``` + +It will look something like this: + +~~~admonish example collapsible=true title="Console output" +``` +./nym-gateway sign --id upgrade_test --contract-msg 2Mf8xYytgEeyJke9LA7TjhHoGQWNBEfgHZtTyy2krFJfGHSiqy7FLgTnauSkQepCZTqKN5Yfi34JQCuog9k6FGA2EjsdpNGAWHZiuUGDipyJ6UksNKRxnFKhYW7ri4MRduyZwbR98y5fQMLAwHne1Tjm9cXYCn8McfigNt77WAYwBk5bRRKmC34BJMmWcAxphcLES2v9RdSR68tkHSpy2C8STfdmAQs3tZg8bJS5Qa8pQdqx14TnfQAPLk3QYCynfUJvgcQTrg29aqCasceGRpKdQ3Tbn81MLXAGAs7JLBbiMEAhCezAr2kEN8kET1q54zXtKz6znTPgeTZoSbP8rzf4k2JKHZYWrHYF9JriXepuZTnyxAKAxvGFPBk8Z6KAQi33NRQkwd7MPyttatHna6kG9x7knffV6ebGzgRBf7NV27LurH8x4L1uUXwm1v1UYCA1WSBQ9Pp2JW69k5v5v7G9gBy8RUcZnMbeL26Qqb8WkuGcmuHhaFfoqSfV7PRHPpPT4M8uRqUyR4bjUtSJJM1yh6QSeZk9BEazzoJqPeYeGoiFDZ3LMj2jesbJweQR4caaYuRczK92UGSSqu9zBKmE45a + + + _ __ _ _ _ __ ___ + | '_ \| | | | '_ \ _ \ + | | | | |_| | | | | | | + |_| |_|\__, |_| |_| |_| + |___/ + + (nym-gateway - version {{platform_release_version}}) + + +>>> 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"}}}} +``` +~~~ + +* Copy the resulting signature: + +``` +>>> 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) + +* 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. + +### Running your gateway +The `run` command starts the gateway: + +``` +./nym-gateway run --id +``` +## Maintenance + +For gateway upgrade, firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md) + diff --git a/documentation/operators/src/nodes/maintenance.md b/documentation/operators/src/nodes/maintenance.md new file mode 100644 index 0000000000..c955975118 --- /dev/null +++ b/documentation/operators/src/nodes/maintenance.md @@ -0,0 +1,690 @@ +# Maintenance + +## Useful commands + +> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +**build-info** + +A `build-info` command prints the build information like commit hash, rust version, binary version just like what command `--version` does. However, you can also specify an `--output=json` flag that will format the whole output as a json, making it an order of magnitude easier to parse. + +For example `./target/debug/nym-network-requester --no-banner build-info --output json` will return: + +``` +{"binary_name":"nym-network-requester","build_timestamp":"2023-07-24T15:38:37.00657Z","build_version":"1.1.23","commit_sha":"c70149400206dce24cf20babb1e64f22202672dd","commit_timestamp":"2023-07-24T14:45:45Z","commit_branch":"feature/simplify-cli-parsing","rustc_version":"1.71.0","rustc_channel":"stable","cargo_profile":"debug"} +``` + +## Upgrading your node + +> The process is the similar for mix node, gateway and network requester. In the following steps we use a placeholder `` 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///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. + +> In case of a network requester this is all 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 `` 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 +* Navigate to the `Bonding` page and click the `Node Settings` link in the top right corner: + +![Bonding page](../images/wallet-screenshots/bonding.png) + +* Update the fields in the `Node Settings` page and click `Submit changes to the blockchain`. + +![Node Settings Page](../images/wallet-screenshots/node_settings.png) + +### Updating node information via the CLI +If you want to bond your `` via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.md#upgrade-a-mix-node) docs. + + +### Upgrading Network Requester to >= v1.1.10 from +``` + +Copy the old keys from your client to the network-requester configuration that was created above: + +``` +cp -vr ~/.nym/clients/myoldclient/data/* ~/.nym/service-providers/network-requester//data +``` + +Edit the configuration to match what you used on your client. Specifically, edit the configuration file at: + +``` +~/.nym/service-providers/network-requester//config/config.toml +``` + +Ensure that the fields `gateway_id`, `gateway_owner`, `gateway_listener` in the new config match those in the old client config at: + +``` +~/.nym/clients/myoldclient/config/config.toml +``` + +### Upgrading your validator + +Upgrading from `v0.31.1` -> `v0.32.0` process is fairly simple. Grab the `v0.32.0` release tarball from the [`nyxd` releases page](https://github.com/nymtech/nyxd/releases), and untar it. Inside are two files: + +- the new validator (`nyxd`) v0.32.0 +- the new wasmvm (it depends on your platform, but most common filename is `libwasmvm.x86_64.so`) + +Wait for the upgrade height to be reached and the chain to halt awaiting upgrade, then: + +* copy `libwasmvm.x86_64.so` to the default LD_LIBRARY_PATH on your system (on Ubuntu 20.04 this is `/lib/x86_64-linux-gnu/`) replacing your existing file with the same name. +* swap in your new `nyxd` binary and restart. + +You can also use something like [Cosmovisor](https://github.com/cosmos/cosmos-sdk/tree/main/tools/cosmovisor) - grab the relevant information from the current upgrade proposal [here](https://nym.explorers.guru/proposal/9). + +Note: Cosmovisor will swap the `nyxd` binary, but you'll need to already have the `libwasmvm.x86_64.so` in place. + +#### Common reasons for your validator being jailed + +The most common reason for your validator being jailed is that your validator is out of memory because of bloated syslogs. + +Running the command `df -H` will return the size of the various partitions of your VPS. + +If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process. + +## 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 `` of your desire. + +* Pause your node process. + +Assuming both machines are remote VPS. + +* Make sure your `~/.ssh/.pub` is in both of the machines `~/.ssh/authorized_keys` file +* Create a `mixnodes` folder in the target VPS. Ssh in from your terminal and run: +``` +# in case none of the nym configs was created previously +mkdir ~/.nym + +#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: +``` +scp -r -3 @:~/.nym/mixnodes/ @:~/.nym/mixnodes/ +``` +* Re-run init (remember that init doesn't overwrite existing keys) to generate a config with the new listening address etc. +* Change the node smart contract info via the wallet interface. Otherwise the keys will point to the old IP address in the smart contract, and the node will not be able to be connected, and it will fail up-time checks. +* Re-run the node from the new location. + +## VPS Setup and Automation +### Configure your firewall +Although your `` is now ready to receive traffic, your server may not be. The following commands will allow you to set up a firewall using `ufw`. + +``` +# check if you have ufw installed +ufw version + +# if it is not installed, install with +sudo apt install ufw -y + +# enable ufw +sudo ufw enable + +# check the status of the firewall +sudo ufw status +``` + +Finally open your `` p2p port, as well as ports for ssh and ports for verloc and measurement pings: + +``` +# for mix node +sudo ufw allow 1789,1790,8000,22/tcp + +# for gateway +sudo ufw allow 1789,22,9000/tcp + +# for network requester +sudo ufw allow 22,9000/tcp + +# for validator +sudo ufw allow 1317,26656,26660,22,80,443/tcp +``` + +Check the status of the firewall: +``` +sudo ufw status +``` + +For more information about your node's port configuration, check the [port reference table](https://nymtech.net/docs/nodes/gateway-setup.html#ports) below. + +### Automating your node with nohup, tmux and systemd + +Although it’s not totally necessary, it's useful to have the mix node automatically start at system boot time. + +#### nohup + +`nohup` is a command with which your terminal is told to ignore the `HUP` or 'hangup' signal. This will stop the node process ending if you kill your session. + +``` +nohup ./ run --id # where `` is the id you set during the `init` command and depends on which node you starting +``` + +#### tmux + +One way is to use `tmux` shell on top of your current VPS terminal. Tmux is a terminal multiplexer, it allows you to create several terminal windows and panes from a single terminal. Processes started in `tmux` keep running after closing the terminal as long as the given `tmux` window was not terminated. + +Use the following command to get `tmux`. + +Platform|Install Command +---|--- +Arch Linux|`pacman -S tmux` +Debian or Ubuntu|`apt install tmux` +Fedora|`dnf install tmux` +RHEL or CentOS|`yum install tmux` +macOS (using Homebrew|`brew install tmux` +macOS (using MacPorts)|`port install tmux` +openSUSE|`zypper install tmux` + +In case it didn't work for your distribution, see how to build `tmux` from [version control](https://github.com/tmux/tmux#from-version-control). + +**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 `` run on its own on the VPS. + +* Pause your `` +* Start tmux with the command +``` +tmux +``` +* The tmux terminal should open in the same working directory, just the layout changed into tmux default layout. +* Start the `` again with a command: +``` +./ run --id +``` +* Now, without closing the tmux window, you can close the whole terminal and the `` (and any other process running in tmux) will stay active. +* Next time just start your teminal, ssh into the VPS and run the following command to attach back to your previous session: +``` +tmux attach-session +``` +* To see keybinding options of tmux press `ctrl`+`b` and after 1 second `?` + +#### systemd + +Here's a systemd service file to do that: + +##### For mix node + +```ini +[Unit] +Description=Nym Mixnode ({{platform_release_version}}) +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User= +LimitNOFILE=65536 +ExecStart=/home///nym-mixnode run --id +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +* Put the above file onto your system at `/etc/systemd/system/nym-mixnode.service`. + +##### For Gateway + +```ini +[Unit] +Description=Nym Gateway ({{platform_release_version}}) +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User=nym +LimitNOFILE=65536 +ExecStart=/home///nym-gateway run --id +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +* Put the above file onto your system at `/etc/systemd/system/nym-gateway.service`. + +##### For Network requester + +```ini +[Unit] +Description=Nym Network Requester ({{platform_release_version}}) +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User=nym # replace this with whatever user you wish +LimitNOFILE=65536 +# remember to add the `--enable-statistics` flag if running as part of a service grant and check the path to your nym-network-requester binary +ExecStart=/home/nym/nym-network-requester run --id +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +Now enable and start your requester: + +``` +systemctl enable nym-network-requester.service +systemctl start nym-network-requester.service + +# you can always check your requester has succesfully started with: +systemctl status nym-network-requester.service +``` +* Put the above file onto your system at `/etc/systemd/system/nym-network-requester.service`. + +##### For Validator + +Below is a systemd unit file to place at `/etc/systemd/system/nymd.service`: + +```ini +[Unit] +Description=Nyxd +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User=nyx # change to your user +Type=simple +Environment="LD_LIBRARY_PATH=/home/youruser/path/to/nyx/binaries" # change to correct path +ExecStart=/home/youruser/path/to/nyx/binaries/nymd start # change to correct path +Restart=on-failure +RestartSec=30 +LimitNOFILE=infinity + +[Install] +WantedBy=multi-user.target +``` + +Proceed to start it with: + +``` +systemctl daemon-reload # to pickup the new unit file +systemctl enable nymd # to enable the service +systemctl start nymd # to actually start the service +journalctl -f # to monitor system logs showing the service start +``` + +##### Following steps for Nym Mixnet nodes + +Change the `` in `ExecStart` to point at your `` binary (`nym-mixnode`, `nym-gateway` or `nym-network-requester`), and the `` so it is the user you are running as. + +If you have built nym in the `$HOME` directory on your server, and your username is `jetpanther`, then the start command for nym mixnode might look like this: + +`ExecStart=/home/jetpanther/nym/target/release/nym-mixnode run --id `. Basically, you want the full `/path/to/nym-mixnode run --id whatever-your-node-id-is` + +Then run: + +``` +# for mix node +systemctl enable nym-mix node.service + +# for gateway +systemctl enable nym-gateway.service +``` + +Start your node: + +``` +# for mix node +service nym-mixnode start + +# for gateway +service nym-gateway start + +``` + +This will cause your node to start at system boot time. If you restart your machine, the node will come back up automatically. + +You can also do `service stop` or `service restart`. + +Note: if you make any changes to your systemd script after you've enabled it, you will need to run: + +``` +systemctl daemon-reload +``` + +This lets your operating system know it's ok to reload the service configuration. + +### Setting the ulimit + +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. + +If you see errors such as: + +``` +Failed to accept incoming connection - Os { code: 24, kind: Other, message: "Too many open files" } +``` + +This means that the operating system is preventing network connections from being made. + +#### Set the ulimit via `systemd` service file + +> Replace `` 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. + +Query the `ulimit` of your `` with: + +``` +# for nym-, nym-gateway and nym-network requester: +grep -i "open files" /proc/$(ps -A -o pid,cmd|grep | grep -v grep |head -n 1 | awk '{print $1}')/limits + +# for nyx validator: +grep -i "open files" /proc/$(ps -A -o pid,cmd|grep nymd | grep -v grep |head -n 1 | awk '{print $1}')/limits +``` + + + +You'll get back the hard and soft limits, which looks something like this: + +``` +Max open files 65536 65536 files +``` + +If your output is **the same as above**, your node will not encounter any `ulimit` related issues. + +However if either value is `1024`, you must raise the limit via the systemd service file. Add the line: + +``` +LimitNOFILE=65536 +``` + +Reload the daemon: + +``` +systemctl daemon-reload +``` + +or execute this as root for system-wide setting of `ulimit`: + +``` +echo "DefaultLimitNOFILE=65535" >> /etc/systemd/system.conf +``` + +Reboot your machine and restart your node. When it comes back, use: +``` +# for nym-mixnode, nym-gateway and nym-network requester: +cat /proc/$(pidof )/limits | grep "Max open files"` to make sure the limit has changed to 65535. + +# for validator +cat /proc/$(pidof nym-validator)/limits | grep "Max open files" +``` +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: + +``` +ulimit -a + +# watch for the output line -n +-n: file descriptors 1024 +``` + +You can change it either by running a command: + +``` +ulimit -u -n 4096 +``` + +or editing `etc/security/conf` and add the following lines: + +``` +# Example hard limit for max opened files +username hard nofile 4096 + +# Example soft limit for max opened files +username soft nofile 4096 +``` + +Then reboot your server and restart your mix node. + +## Virtual IPs and hosting via Google & AWS +For true internet decentralization we encourage operators to use diverse VPS providers instead of the largest companies offering such services. If for some reasons you have already running AWS or Google and want to setup a `` there, please read the following. + +On some services (AWS, Google, etc) the machine's available bind address is not the same as the public IP address. In this case, bind `--host` to the local machine address returned by `$(curl ifconfig.me)`, but also specify `--announce-host` with the public IP. Please make sure that you pass the correct, routable `--announce-host`. + +For example, on a Google machine, you may see the following output from the `ifconfig` command: + +``` +ens4: flags=4163 mtu 1460 + inet 10.126.5.7 netmask 255.255.255.255 broadcast 0.0.0.0 + ... +``` + +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. + +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: +``` +./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. + +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 + +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 `` 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. + +Using this API endpoint returns information about the Reward Estimation: + +``` +/status/mixnode//reward-estimation +``` + +Query Response: + +``` + "estimation": { + "total_node_reward": "942035.916721770541325331", + "operator": "161666.263307386408152071", + "delegates": "780369.65341438413317326", + "operating_cost": "54444.444444444444444443" + }, +``` + +> 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_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_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. + +### Validator: Installing and configuring nginx for HTTPS +#### Setup +[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.) is an open source software used for operating high-performance web servers. It allows us to set up reverse proxying on our validator server to improve performance and security. + +Install `nginx` and allow the 'Nginx Full' rule in your firewall: + +``` +sudo ufw allow 'Nginx Full' +``` + +Check nginx is running via systemctl: + +``` +systemctl status nginx +``` + +Which should return: + +``` +● nginx.service - A high performance web server and a reverse proxy server + Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) + Active: active (running) since Fri 2018-04-20 16:08:19 UTC; 3 days ago + Docs: man:nginx(8) + Main PID: 2369 (nginx) + Tasks: 2 (limit: 1153) + CGroup: /system.slice/nginx.service + ├─2369 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; + └─2380 nginx: worker process +``` + +#### Configuration + +Proxying your validator's port `26657` to nginx port `80` can then be done by creating a file with the following at `/etc/nginx/conf.d/validator.conf`: + +``` +server { + listen 80; + listen [::]:80; + server_name "domain_name"; + + location / { + proxy_pass http://127.0.0.1:26657; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +Followed by: + +``` +sudo apt install certbot nginx python3 +certbot --nginx -d nym-validator.yourdomain.com -m you@yourdomain.com --agree-tos --noninteractive --redirect +``` + +```admonish caution title="" +If using a VPS running Ubuntu 20: replace `certbot nginx python3` with `python3-certbot-nginx` +``` + +These commands will get you an https encrypted nginx proxy in front of the API. + +### Configuring Prometheus metrics (optional) + +Configure Prometheus with the following commands (adapted from NodesGuru's [Agoric setup guide](https://nodes.guru/agoric/setup-guide/en)): + +``` +echo 'export OTEL_EXPORTER_PROMETHEUS_PORT=9464' >> $HOME/.bashrc +source ~/.bashrc +sed -i '/\[telemetry\]/{:a;n;/enabled/s/false/true/;Ta}' $HOME/.nymd/config/app.toml +sed -i "s/prometheus-retention-time = 0/prometheus-retention-time = 60/g" $HOME/.nymd/config/app.toml +sudo ufw allow 9464 +echo 'Metrics URL: http://'$(curl -s ifconfig.me)':26660/metrics' +``` + +Your validator's metrics will be available to you at the returned 'Metrics URL'. + +~~~admonish example collapsible=true title="Console output" +``` +# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 6.7969e-05 +go_gc_duration_seconds{quantile="0.25"} 7.864e-05 +go_gc_duration_seconds{quantile="0.5"} 8.4591e-05 +go_gc_duration_seconds{quantile="0.75"} 0.000115919 +go_gc_duration_seconds{quantile="1"} 0.001137591 +go_gc_duration_seconds_sum 0.356555301 +go_gc_duration_seconds_count 2448 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 668 +# HELP go_info Information about the Go environment. +# TYPE go_info gauge +go_info{version="go1.15.7"} 1 +# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# TYPE go_memstats_alloc_bytes gauge +go_memstats_alloc_bytes 1.62622216e+08 +# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. +# TYPE go_memstats_alloc_bytes_total counter +go_memstats_alloc_bytes_total 2.09341707264e+11 +# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. +# TYPE go_memstats_buck_hash_sys_bytes gauge +go_memstats_buck_hash_sys_bytes 5.612319e+06 +# HELP go_memstats_frees_total Total number of frees. +# TYPE go_memstats_frees_total counter +go_memstats_frees_total 2.828263344e+09 +# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started. +# TYPE go_memstats_gc_cpu_fraction gauge +go_memstats_gc_cpu_fraction 0.03357798610671518 +# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. +# TYPE go_memstats_gc_sys_bytes gauge +go_memstats_gc_sys_bytes 1.3884192e+07 +``` +~~~ + +## Ports +All ``-specific port configuration can be found in `$HOME/.nym///config/config.toml`. If you do edit any port configs, remember to restart your client and node processes. + +### Mix node port reference +| Default port | Use | +| ------------ | ------------------------- | +| `1789` | Listen for Mixnet traffic | +| `1790` | Listen for VerLoc traffic | +| `8000` | Metrics http API endpoint | + + +### Gateway port reference +| Default port | Use | +|--------------|---------------------------| +| `1789` | Listen for Mixnet traffic | +| `9000` | Listen for Client traffic | + +### Network requester port reference + +| Default port | Use | +|--------------|---------------------------| +| `9000` | Listen for Client traffic | + +### Validator port reference +All validator-specific port configuration can be found in `$HOME/.nymd/config/config.toml`. If you do edit any port configs, remember to restart your validator. + +| Default port | Use | +|--------------|--------------------------------------| +| 1317 | REST API server endpoint | +| 26656 | Listen for incoming peer connections | +| 26660 | Listen for Prometheus connections | + +/ diff --git a/documentation/operators/src/nodes/mix-node-setup.md b/documentation/operators/src/nodes/mix-node-setup.md new file mode 100644 index 0000000000..a50c5cac13 --- /dev/null +++ b/documentation/operators/src/nodes/mix-node-setup.md @@ -0,0 +1,309 @@ +# 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. + +> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets. + +## Current version +``` + +``` + +The `nym-mix node` binary is currently one point version ahead of the rest of the platform binaries due to a patch applied between releases. + +## Preliminary steps + +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. + +To begin, move to `/target/release` directory from which you run the node commands: + +``` +cd target/release +``` + +### Viewing command help + +You can check that your binaries are properly compiled with: + +``` +./nym-mixnode --help +``` + +Which should return a list of all available commands. + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +You can also check the various arguments required for individual commands with: + +``` +./nym-mixnode --help +``` + +> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +### Initialising your mix node + +To check available configuration options for initializing your node use: + +``` +./nym-mixnode init --help +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +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 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 --host $(curl ifconfig.me) +``` + + +~~~admonish example collapsible=true title="Console output" +``` +.nym-mixnode init --id --host $(curl ifconfig.me) --wallet-address + + +Initialising mixnode ... +Saved mixnet identity and sphinx keypairs + 2023-06-04T08:20:32.862Z INFO nym_config > Configuration file will be saved to "/home//.nym/mixnodes//config/config.toml" +Saved configuration file to "/home//.nym/mixnodes//config/config.toml" +Mixnode configuration completed. + + _ __ _ _ _ __ ___ + | '_ \| | | | '_ \ _ \ + | | | | |_| | | | | | | + |_| |_|\__, |_| |_| |_| + |___/ + + (nym-mixnode - version {{platform_release_version}}) + + +Identity Key: DhmUYedPZvhP9MMwXdNpPaqCxxTQgjAg78s2nqtTTiNF","version":"{{platform_release_version}}"},"cost_params +Sphinx Key: CfZSy1jRfrfiVi9JYexjFWPqWkKoY72t7NdpWaq37K8Z +Host: 62.240.134.189 (bind address: 62.240.134.189) +Version: {{platform_release_version}} +Mix Port: 1789, Verloc port: 1790, Http Port: 8000 +``` +~~~ + +> 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//`. + +### 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 --contract-msg +``` + +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 {{platform_release_version}}) + + +>>> 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 +``` + +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: + +``` +./nym-mixnode describe --id +``` +Node description is a short text that describes your node. It is displayed in the `./nym-mixnode list` command and in the `./nym-mixnode node-details --id ` command. It also shows up in the node explorer to let people know what your node is about and link to your website. + +You can set your node description, by creating a file called `description.toml` and put it in the same directory as your `config.toml` file (`~/.nym/mixnodes//description.toml`). The file should look like this example: + +```toml +name = "Winston Smith" +description = "I am the Sphinx" +link = "https://nymtech.net" +location = "Giza, Egypt" +``` + +> Remember to restart your mix node process in order for the new description to be propagated. + +## 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. + +### 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. + +You can use either `nym-cli` which can be downloaded from the [release page](https://github.com/nymtech/nym/releases) or compiling `nyxd`. + + +Change directory by `cd ///` and run the following on the family head to obtain the signature for the member: + +``` +./nym-mixnode sign --id --text +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +Using `nym-cli`: + +> `--mnemonic` is the mnemonic of the member wanting to be the head of family. + +``` +/nym-cli cosmwasm execute '{"create_family": {"signature": "","family_head": "","owner_signature":"","label": ""}}' --mnemonic +``` + +Using `nyxd`: + +> `--from` is mnemonic of the member wanting to join the family. + +``` +./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block +``` + +To get the node owner signature, use: + +`./nym-mixnode node-details --id ` + +### Joining a Node Family + +Change directory by `cd ///` and run the following on the family head to obtain the signature for the member: + +``` +./nym-mixnode sign --id --text +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +Using `nym-cli`: + +``` +./nym-cli cosmwasm execute '{"join_family": {"signature": "","family_head": "","owner_signautre": "", "label":""}}' --mnemonic +``` + +Using `nyxd`: + +``` +./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block +``` + + +To get the node owner signature, use: + +`./nym-mixnode node-details --id ` + + +### Leaving a family +If wanting to leave, run the same initial command as above, followed by: + +Using `nym-cli`: + +``` +./nym-cli cosmwasm execute '{"leave_family": {"signature": "","family_head": "","owner_signautre": ""}}' --mnemonic +``` + +Using `nyxd`: + +``` +./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block +``` + +## 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: + +- [Mainnet](https://explorer.nymtech.net/overview) +- [Sandbox testnet](https://sandbox-explorer.nymtech.net/) + +Enter your **identity key** to find your node. There are numerous statistics about your node on that page that are useful for checking your up-time history, packets mixed, and any delegations your node may have. + +There are also 2 community explorers which have been created by [Nodes Guru](https://nodes.guru): + +- [Mainnet](https://mixnet.explorers.guru/) +- [Sandbox testnet](https://sandbox.mixnet.explorers.guru/) + +For more details see [Troubleshooting FAQ](../nodes/troubleshooting.md) + + + +## Maintenance + +For mix node upgrade, firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md) + diff --git a/documentation/docs/src/nodes/network-requester-setup.md b/documentation/operators/src/nodes/network-requester-setup.md similarity index 79% rename from documentation/docs/src/nodes/network-requester-setup.md rename to documentation/operators/src/nodes/network-requester-setup.md index 6fe3ef7971..2082c2035f 100644 --- a/documentation/docs/src/nodes/network-requester-setup.md +++ b/documentation/operators/src/nodes/network-requester-setup.md @@ -1,23 +1,28 @@ # 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 on this page, go there first. +> The Nym gateway 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. ## Current version ``` ``` +## Preliminary steps + +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. -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//`) 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//`) as a whitelist for outbound requests. 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 [here](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) automatically. +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. @@ -95,12 +100,18 @@ alephium.org ``` ## Network Requester Directory -You can find a list of Network Requesters running the default whitelist [here](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. ## Viewing command help +To begin, move to `/target/release` directory from which you run the node commands: + +``` +cd target/release +``` + The `./nym-network-requester --help ` command can be used to show a list of available parameters. ~~~admonish example collapsible=true title="Console output" @@ -112,17 +123,21 @@ The `./nym-network-requester --help ` command can be used to show a list of avai You can check the required parameters for available commands by running: ``` -./nym-network-requester --help +./nym-network-requester --help ``` +> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + ## 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 choosing. 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 `` using the `--id` command and give it a value of your choice. The following command will achieve that: ``` - ./nym-network-requester init --id example + ./nym-network-requester init --id ``` +In the following we used `example`. + ~~~admonish example collapsible=true title="Console output" ``` @@ -133,15 +148,12 @@ The network-requester needs to be initialized before it can be run. This is requ Now that we have initialized our network-requester, we can start it with the following command: ``` - ./nym-network-requester run --id example + ./nym-network-requester run --id ``` -## Upgrading your network requester -You can upgrade your network requester by following these steps: +## Maintenance -* stop your network requester service -* replace the old binary with the new binary -* restart your service using the commands in the previous section of the document +For network requester upgrade (including an upgrade from `= v1.1.10`), firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md). ### Upgrading to >= v1.1.10 from >>>>>> release/v1.1.27:documentation/docs/src/nodes/network-requester-setup.md +>>>>>>> 85ab634d9c1f1f54073c97a133c83e645a0a3f41 ## Using your network requester @@ -254,7 +268,7 @@ ls $HOME/.nym/service-providers/network-requester/ # 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](../clients/socks5-client.md) (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 restart your 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 restart your 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` @@ -262,7 +276,7 @@ We already know that `allowed.list` is what lets requests go through. All unknow 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. Add `nymtech.net` to your `allowed.list` (remember to restart 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. diff --git a/documentation/operators/src/nodes/setup-guides.md b/documentation/operators/src/nodes/setup-guides.md new file mode 100644 index 0000000000..ca86dd99f4 --- /dev/null +++ b/documentation/operators/src/nodes/setup-guides.md @@ -0,0 +1,11 @@ +# Node Setup Guides + +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) +* [Gateway](./gateway-setup.md) +* [Network Requester](./network-requester-setup.md) +* [Validator](./validator-setup.md) + + diff --git a/documentation/docs/src/nodes/troubleshooting.md b/documentation/operators/src/nodes/troubleshooting.md similarity index 54% rename from documentation/docs/src/nodes/troubleshooting.md rename to documentation/operators/src/nodes/troubleshooting.md index e832edbc39..20b98970df 100644 --- a/documentation/docs/src/nodes/troubleshooting.md +++ b/documentation/operators/src/nodes/troubleshooting.md @@ -1,6 +1,5 @@ # Troubleshooting - ## Binary Build Problems ### I am trying to build from the GitHub archive files and the build fails @@ -20,9 +19,19 @@ Why does this happen? We have scripts which automatically include the Git commit hash and Git tag in the binary for easier debugging later. If you download a .zip and try building from that, it's not a Git repository and build will fail as above. +What to do? + +* Open terminal in the directory where you want to have a git repository +* To get Nym repository for the first time, run: +``` +git clone https://github.com/nymtech/nym.git +``` +* Follow the instructions to build the platform +* To upgrade, pause your nodes, in the same terminal window run `git pull`, follow the upgrade instructions and re-start your nodes. + ## General Node Config -### Where can I find my private and public keys and config? +### Where can I find my private and public keys and config? All config and keys files are stored in a directory named after your `id` which you chose during the `init` process, and can be found at the following PATH: `$HOME/.nym//` where `$HOME` is a home directory of the user (your current user in this case) that launched the node or client. @@ -41,7 +50,7 @@ bob@nym:~$ tree /home/nym/.nym/mixnodes/ | `-- public_sphinx.pem ``` -> If you `cat` the `public_sphinx.pem key`, the output will be different from the public key you will see on Nym [dashboard](https://sandbox-explorer.nymtech.net/). The reason for this is that `.pem` files are encoded in **base64**, however on the web they are in **base58**. Don't be confused if your keys look different. They are the same keys, just with different encoding :) +> If you `cat` the `public_sphinx.pem` key, the output will be different from the public key you will see on Nym [dashboard](https://sandbox-explorer.nymtech.net/). The reason for this is that `.pem` files are encoded in **base64**, however on the web they are in **base58**. Don't be confused if your keys look different. They are the same keys, just with different encoding :) ## Mix Nodes @@ -49,15 +58,17 @@ bob@nym:~$ tree /home/nym/.nym/mixnodes/ ### How can I tell my node is up and running and mixing traffic? First of all check the 'Mixnodes' section of either the Nym Network Explorers: -* [Mainnet](https://explorer.nymtech.net/overview) +* [Mainnet](https://explorer.nymtech.net/) * [Sandbox testnet](https://sandbox-explorer.nymtech.net/) -Enter your **identity key** to find your node. Check the contents of the 'mixnode stats' and 'uptime story' sections. +Enter your **identity key** to find your node. Check the contents of the `Mixnode stats` and `Routing score` sections. There are 2 community explorers currently, which have been created by [Nodes Guru](https://nodes.guru): * [Mainnet](https://mixnet.explorers.guru/) * [Sandbox testnet](https://sandbox.mixnet.explorers.guru/) +[Here](https://github.com/cosmos/chain-registry/blob/master/nyx/chain.json#L158-L187) is a dictionary with Nyx chain registry entry regarding all explorers. + If you want more information, or if your node isn't showing up on the explorer of your choice and you want to double-check, here are some examples on how to check if the node is configured properly. #### Check from your VPS @@ -67,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 mixnode 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. @@ -79,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 mixnode port +# run against mix node port sudo lsof -i TCP:1789 # if you have specified a different port in your mixnode config, change accordingly ``` @@ -99,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 mixnode 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! @@ -111,6 +122,16 @@ If you have created `nym-mixnode.service` file (i.e. you are running your mixnod You can add ` | tail` to the end of the command to watch for new entries in real time if needed. +##### build-info + +A `build-info` command prints the build information like commit hash, rust version, binary version just like what command `--version` does. However, you can also specify an `--output=json` flag that will format the whole output as a json, making it an order of magnitude easier to parse. + +For example `./target/debug/nym-network-requester --no-banner build-info --output json` will return: + +``` +{"binary_name":"nym-network-requester","build_timestamp":"2023-07-24T15:38:37.00657Z","build_version":"1.1.23","commit_sha":"c70149400206dce24cf20babb1e64f22202672dd","commit_timestamp":"2023-07-24T14:45:45Z","commit_branch":"feature/simplify-cli-parsing","rustc_version":"1.71.0","rustc_channel":"stable","cargo_profile":"debug"} +``` + #### Check from your local machine ##### Scan ports with `nmap`: @@ -119,7 +140,7 @@ You can add ` | tail` to the end of the command to watch for new entries in real nmap -p 1789 -Pn ``` -If your mixnode 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 @@ -138,58 +159,39 @@ 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 gatways on the Sandbox testnet by replacing `validator` with `sandbox-validator`. +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`. #### 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 mixnode, `report` and `history`. Find more information about this in the [Mixnodes metrics documentation](../nodes/mix-node-setup.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? If you are still unable to see your node on the dashboard, or your node is declaring it has not mixed any packets, there are several potential issues: -- The firewall on your host machine is not configured properly. +- 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 mixnode from a VPS without IPv6 support. -- You did not use the `--announce-host` flag while running the mixnode from your local machine behind NAT. -- You did not configure your router firewall while running the mixnode from your local machine behind NAT, or you are lacking IPv6 support. -- Your mixnode is not running at all, it has either exited / panicked or you closed the session without making the node persistent. +- 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 mixnode **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! -``` - -#### Incorrectly configured firewall - -The most common reason your mixnode might not be mixing packets is due to a poorly configured firewall. The following commands will allow you to set up a firewall using `ufw`. - -``` -# check if you have ufw installed -ufw version -# if it is not installed, install with -sudo apt install ufw -y -# enable ufw -sudo ufw enable -# check the status of the firewall -sudo ufw status -``` - -Finally open your mixnode's p2p port, as well as ports for ssh, http, and https connections, and ports `8000` and `1790` for verloc and measurement pings: - -``` -sudo ufw allow 1789,1790,8000,22,80,443/tcp -# check the status of the firewall -sudo ufw status +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 mixnode in the web wallet [interface](https://sandbox-wallet.nymtech.net/). When in doubt, unbond and then rebond 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. #### 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 mixnode 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. #### No IPv6 connectivity @@ -220,87 +222,33 @@ 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 mixnode 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 a 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/). 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! +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 mixnode 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. +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 to create the mixnode config. +Here is a sample of the `init` command example to create the mix node config. ``` -./target/release/nym-mixnode init --id nym-nat --host 0.0.0.0 --announce-host 85.160.12.13 --layer 3 +./nym-mixnode init --id --host 0.0.0.0 --announce-host 85.160.12.13 ``` -- `--host 0.0.0.0` should work everytime even if your local machine IPv4 address changes. For example on Monday your router gives your machine an address `192.168.0.13` and on Wednesday, the DHCP lease will end and you will be asigned `192.168.0.14`. Using `0.0.0.0` should avoid this without having to set any static ip in your router`s configuration. +- `--host 0.0.0.0` should work every time even if your local machine IPv4 address changes. For example on Monday your router gives your machine an address `192.168.0.13` and on Wednesday, the DHCP lease will end and you will be assigned `192.168.0.14`. Using `0.0.0.0` should avoid this without having to set any static IP in your router`s configuration. - you can get your current IPv4 address by either using `curl ipinfo.io` if you're on MacOS or Linux or visiting [whatsmyip site](https://www.whatsmyip.org/). Simply copy it and use it as `--anounce-host` address. -Make sure you check if your node is really mixing. You will need a bit of luck to set this up from your home behind NAT. +Make sure you check if your node is really mixing. We are aiming to improve the setup for operators running locally, however you may need a bit of patience to set this up from your home behind NAT. ### 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 mixnode process! There are multiple ways on how to make it persistent even after exiting your ssh session, the easiest solution is to use `nohup`, and the more elegant solution is to run the node with `systemd`. - -### Running your mixnode as a background process with `nohup` - -`nohup` is a command with which your terminal is told to ignore the `HUP` or 'hangup' signal. This will stop the mixnode process ending if you kill your session. - -``` -nohup ./nym-mixnode run --id NYM # where `--id NYM` is the id you set during the `init` command. -``` - -### Running your mixnode as a background process with `systemd` - -The most reliable and elegant solution is to create a `systemd.service` file and run the nym-mixnode with `systemctl` command. - -Create a file with `nano` at `/etc/systemd/system/nym-mixnode.service` containing the following: - -```ini -[Unit] -Description=nym mixnode service -After=network.target - -[Service] -Type=simple -User=nym # change as appropriate -LimitNOFILE=65536 -ExecStart=/home/nym/nym-mixnode run --id nym # change as appropriate -KillSignal=SIGINT -Restart=on-failure -RestartSec=30 -Restart=on-abort -[Install] -WantedBy=multi-user.target -``` - -``` -# enable the service -sudo systemctl enable nym-mixnode -# start the service -sudo systemctl start nym-mixnode -# check if the service is running properly and mixnode is mixing -sudo systemctl status nym-mixnode -``` - -Now your node should be mixing all the time, and restart if you reboot your server! - -Anytime you change your `systemd` service file you need to `sudo systemctl daemon-reload` in order to restart the service. - -### Network configuration seems fine but log still claims `Since startup mixed 0 packets!` - -This behavior is most likely caused by a mismatch between your node configuration and the bonding information. Unbond and then rebond your node. - -Also make sure to enter all the information in the web wallet exactly as it appears in the log when you start the mixnode process. In particular, the `host` field must contain the _port_ on which your mixnode will listen: - -- correct host: `34.12.3.43:1789` -- incorrect host:`34.12.3.43` +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 -Most of the `ERROR` and `WARN` messages in your node logs are benign - as long as your node outputs `since startup mixed X packets!` in your logs (and this number increases over time), your node is mixing packets. If you want to be sure, check the Nym [dashboard](https://sandbox-explorer.nymtech.net/) or see other ways on how to check if your node is mixing properly as outlined in the section **How can I tell my node is up and running and mixing traffic?** above. +Most of the `ERROR` and `WARN` messages in your node logs are benign - as long as your node outputs `since startup mixed X packets!` (`X` bust be > 0) in your logs (and this number increases over time), your node is mixing packets. If you want to be sure, check the Nym [dashboard](https://sandbox-explorer.nymtech.net/) or see other ways on how to check if your node is mixing properly as outlined in the section [**How can I tell my node is up and running and mixing traffic?**](./troubleshooting.md#how-can-i-tell-my-node-is-up-and-running-and-mixing-traffic?) above. More specific errors and warnings are covered below. @@ -312,57 +260,72 @@ If you are running into issues with an error including the following: thread 'tokio-runtime-worker' panicked at 'Failed to create TCP listener: Os { code: 99, kind: AddrNotAvailable, message: "Cannot assign requested address" }' ``` -Then you need to `--announce-host ` and ``--host ` on startup. This issue arises because of your use of a provider like AWS or Google Cloud, and the fact that your VPS' available bind address is not the same as the public IP address (see [Virtual IPs and hosting via Google and AWS](../nodes/mix-node-setup.md#virtual-ips-and-hosting-via-google--aws) for more information on this issue). +Then you need to `--announce-host ` and `--host ` on startup. This issue is addressed [above](./troubleshooting.md#missing-`announce-host`-flag) + + ### Can I use a port other than 1789 ? Yes! Here is what you will need to do: -Assuming you would like to use port `1337` for your mixnode, 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 mixnode'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. -Assuming your node name is `nym`, the config file is located at `~/.nym/mixnodes/nym/config/config.toml`. +The config file is located at `~/.nym/mixnodes//config/config.toml`. + +For example, assuming `` was chosen to be `alice-node`: ``` -nano ~/.nym/mixnodes/nym/config/config.toml +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 mixnode 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 mixnode 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-fakeable 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` onwards. +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. + +## Validators + +### Common reasons for your validator being jailed + +The most common reason for your validator being jailed is that your validator is out of memory because of bloated syslogs. + +Running the command `df -H` will return the size of the various partitions of your VPS. + +If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process. -### Where can I get more help? +## Where can I get more help? -The fastest way to reach one of us or get a help from the community, visit our [Telegram help chat](https://t.me/nymchan_help_chat) or head to our [Discord](https://Discord.gg/nym) +The fastest way to reach one of us or get a help from the community, visit our [Telegram Node Setup Help Chat](https://t.me/nymchan_help_chat) or head to our [Discord](https://Discord.gg/nym). + +For more tech heavy question join our [Matrix core community channel](https://matrix.to/#/#general:nymtech.chat), where you can meet other builders and Nym core team members. -For more tech heavy questions join our Keybase channel. Get Keybase [here](https://keybase.io/), then click Teams -> Join a team. Type nymtech.friends into the team name and hit continue. For general chat, hang out in the #general channel. diff --git a/documentation/docs/src/nodes/validator-setup.md b/documentation/operators/src/nodes/validator-setup.md similarity index 56% rename from documentation/docs/src/nodes/validator-setup.md rename to documentation/operators/src/nodes/validator-setup.md index 053a19253d..ca35c0b2b9 100644 --- a/documentation/docs/src/nodes/validator-setup.md +++ b/documentation/operators/src/nodes/validator-setup.md @@ -1,10 +1,20 @@ # Validators +> 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 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. +> 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. + ## Building your validator + +> Any syntax in `<>` brackets is a user's unique variable. Exchange it with a corresponding name without the `<>` brackets. + ### Prerequisites #### `git`, `gcc`, `jq` + * Debian-based systems: ``` apt install git build-essential jq @@ -20,22 +30,15 @@ pacman -S git gcc jq ``` #### `Go` -`Go` can be installed via the following commands (taken from the [Agoric SDK docs](https://github.com/Agoric/agoric-sdk/wiki/Validator-Guide-for-Incentivized-Testnet#install-go)): +`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 -sudo rm -rf /usr/local/go +# 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 -# Install correct Go version -curl https://dl.google.com/go/go1.20.4.linux-amd64.tar.gz | sudo tar -C/usr/local -zxvf - - -# Update environment variables to include go -cat <<'EOF' >>$HOME/.profile -export GOROOT=/usr/local/go -export GOPATH=$HOME/go -export GO111MODULE=on -export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin -EOF +# Add /usr/local/go/bin to the PATH environment variable +export PATH=$PATH:/usr/local/go/bin source $HOME/.profile ``` @@ -61,7 +64,7 @@ The validator binary can be compiled by running the following commands: ``` git clone https://github.com/nymtech/nyxd.git cd nyxd -git checkout release/ +git checkout release/ # Mainnet make build @@ -76,7 +79,46 @@ At this point, you will have a copy of the `nyxd` binary in your `build/` direct ./build/nyxd ``` -You should see help text print out. +You should see a similar help menu printed to you: + +~~~admonish example collapsible=true title="Console output" +``` +Wasm 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 + +Flags: + -h, --help help for nyxd + --home string directory for config and data (default "/home/willow/.nyxd") + --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` @@ -89,10 +131,10 @@ If you are seeing an error concerning this file when trying to run `nyxd`, then Simply `cp` or `mv` that file to `/lib/x86_64-linux-gnu/` and re-run `nyxd`. ### Adding `nyxd` to your `$PATH` -You'll need to set `LD_LIBRARY_PATH` in your user's `~/.bashrc` file, and add that to our path. Replace `/home/youruser/path/to/nym/binaries` in the command below to the locations of `nyxd` and `libwasmvm.so` and run it. If you have compiled these on the server, they will be in the `build/` folder: +You'll need to set `LD_LIBRARY_PATH` in your user's `~/.bashrc` or `~/.zshrc` file (depends on the terminal you use), and add that to our path. Replace `/home///binaries` in the command below to the locations of `nyxd` and `libwasmvm.so` and run it. If you have compiled these on the server, they will be in the `build/` folder: ``` -NYX_BINARIES=/home/youruser/path/to/validator/binary +NYX_BINARIES=/home/// # if you are using another shell like zsh replace '.bashrc' with the relevant config file echo 'export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:'NYX_BINARIES >> ~/.bashrc @@ -106,7 +148,46 @@ Test everything worked: nyxd ``` -This should return the regular help text. +This should return the regular help menu: + +~~~admonish example collapsible=true title="Console output" +``` +Wasm 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 + +Flags: + -h, --help help for nyxd + --home string directory for config and data (default "/home/willow/.nyxd") + --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. + +``` +~~~ ## Initialising your validator ### Prerequisites: @@ -165,7 +246,7 @@ create_empty_blocks = false laddr = "tcp://0.0.0.0:26656" ``` -These affect the following: +These affect the following: * `persistent_peers = "@.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 @@ -179,7 +260,7 @@ Optionally, if you want to enable [Prometheus](https://prometheus.io/) metrics t And if you wish to add a human-readable moniker to your node: -- `moniker = "yourname"` +- `moniker = ""` Finally, if you plan on using [Cockpit](https://cockpit-project.org/documentation.html) on your server, change the `grpc` port from `9090` as this is the port used by Cockpit. @@ -232,7 +313,9 @@ 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 `//.nymd/config/genesis.json` with that of the correct genesis file. + +### Open firewall ports Before starting the validator, we will need to open the firewall ports: @@ -245,9 +328,9 @@ sudo ufw allow 1317,26656,26660,22,80,443/tcp 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/v0.42/core/grpc_rest.html). +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](#validator-port-reference) below. +For more information about your validator's port configuration, check the [validator port reference table](./maintenance.md#ports) below. > 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. @@ -295,8 +378,8 @@ Once your validator has synced and you have received tokens, you can join consen nyxd tx staking create-validator --amount=10000000unyx --fees=0unyx - --pubkey=$(/home/youruser/path/to/nyxd/binaries/nyxd tendermint show-validator) - --moniker="whatever you called your validator" + --pubkey=$(/home///nyxd/binaries/nyxd tendermint show-validator) + --moniker="" --chain-id=nyx --commission-rate="0.10" --commission-max-rate="0.20" @@ -312,8 +395,8 @@ nyxd tx staking create-validator nyxd tx staking create-validator --amount=10000000unyxt --fees=5000unyxt - --pubkey=$(/home/youruser/path/to/nym/binaries/nyxd tendermint show-validator) - --moniker="whatever you called your validator" + --pubkey=$(/home///nym/binaries/nyxd tendermint show-validator) + --moniker="" --chain-id=sandbox --commission-rate="0.10" --commission-max-rate="0.20" @@ -335,10 +418,10 @@ If you want to edit some details for your node you will use a command like this: # Mainnet nyxd tx staking edit-validator --chain-id=nyx - --moniker="whatever you called your validator" + --moniker="" --details="Nyx validator" - --security-contact="your email" - --identity="your identity" + --security-contact="" + --identity="" --gas="auto" --gas-adjustment=1.15 --from="KEYRING_NAME" @@ -348,10 +431,10 @@ nyxd tx staking edit-validator # Sandbox testnet nyxd tx staking edit-validator --chain-id=sandbox - --moniker="whatever you called your validator" + --moniker="" --details="Sandbox testnet validator" --security-contact="your email" - --identity="your identity" + --identity="" --gas="auto" --gas-adjustment=1.15 --from="KEYRING_NAME" @@ -361,213 +444,18 @@ nyxd tx staking edit-validator 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. ### Automating your validator with systemd -You will most likely want to automate your validator restarting if your server reboots. Below is a systemd unit file to place at `/etc/systemd/system/nymd.service`: - -```ini -[Unit] -Description=Nyxd -StartLimitInterval=350 -StartLimitBurst=10 - -[Service] -User=nyx # change to your user -Type=simple -Environment="LD_LIBRARY_PATH=/home/youruser/path/to/nyx/binaries" # change to correct path -ExecStart=/home/youruser/path/to/nyx/binaries/nymd start # change to correct path -Restart=on-failure -RestartSec=30 -LimitNOFILE=infinity - -[Install] -WantedBy=multi-user.target -``` - -Proceed to start it with: - -``` -systemctl daemon-reload # to pickup the new unit file -systemctl enable nymd # to enable the service -systemctl start nymd # to actually start the service -journalctl -f # to monitor system logs showing the service start -``` +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 -#### Setup -[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.) is an open source software used for operating high-performance web servers. It allows us to set up reverse proxying on our validator server to improve performance and security. -Install `nginx` and allow the 'Nginx Full' rule in your firewall: - -``` -sudo ufw allow 'Nginx Full' -``` - -Check nginx is running via systemctl: - -``` -systemctl status nginx -``` - -Which should return: - -``` -● nginx.service - A high performance web server and a reverse proxy server - Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) - Active: active (running) since Fri 2018-04-20 16:08:19 UTC; 3 days ago - Docs: man:nginx(8) - Main PID: 2369 (nginx) - Tasks: 2 (limit: 1153) - CGroup: /system.slice/nginx.service - ├─2369 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; - └─2380 nginx: worker process -``` - -#### Configuration - -Proxying your validator's port `26657` to nginx port `80` can then be done by creating a file with the following at `/etc/nginx/conf.d/validator.conf`: - -``` -server { - listen 80; - listen [::]:80; - server_name "domain_name"; - - location / { - proxy_pass http://127.0.0.1:26657; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } -} -``` - -Followed by: - -``` -sudo apt install certbot nginx python3 -certbot --nginx -d nym-validator.yourdomain.com -m you@yourdomain.com --agree-tos --noninteractive --redirect -``` - -```admonish caution title="" -If using a VPS running Ubuntu 20: replace `certbot nginx python3` with `python3-certbot-nginx` -``` - -These commands will get you an https encrypted nginx proxy in front of the API. - -### Configuring Prometheus metrics (optional) - -Configure Prometheus with the following commands (adapted from NodesGuru's [Agoric setup guide](https://nodes.guru/agoric/setup-guide/en)): - -``` -echo 'export OTEL_EXPORTER_PROMETHEUS_PORT=9464' >> $HOME/.bashrc -source ~/.bashrc -sed -i '/\[telemetry\]/{:a;n;/enabled/s/false/true/;Ta}' $HOME/.nymd/config/app.toml -sed -i "s/prometheus-retention-time = 0/prometheus-retention-time = 60/g" $HOME/.nymd/config/app.toml -sudo ufw allow 9464 -echo 'Metrics URL: http://'$(curl -s ifconfig.me)':26660/metrics' -``` - -Your validator's metrics will be available to you at the returned 'Metrics URL'. - -~~~admonish example collapsible=true title="Console output" -``` -# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. -# TYPE go_gc_duration_seconds summary -go_gc_duration_seconds{quantile="0"} 6.7969e-05 -go_gc_duration_seconds{quantile="0.25"} 7.864e-05 -go_gc_duration_seconds{quantile="0.5"} 8.4591e-05 -go_gc_duration_seconds{quantile="0.75"} 0.000115919 -go_gc_duration_seconds{quantile="1"} 0.001137591 -go_gc_duration_seconds_sum 0.356555301 -go_gc_duration_seconds_count 2448 -# HELP go_goroutines Number of goroutines that currently exist. -# TYPE go_goroutines gauge -go_goroutines 668 -# HELP go_info Information about the Go environment. -# TYPE go_info gauge -go_info{version="go1.15.7"} 1 -# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. -# TYPE go_memstats_alloc_bytes gauge -go_memstats_alloc_bytes 1.62622216e+08 -# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. -# TYPE go_memstats_alloc_bytes_total counter -go_memstats_alloc_bytes_total 2.09341707264e+11 -# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. -# TYPE go_memstats_buck_hash_sys_bytes gauge -go_memstats_buck_hash_sys_bytes 5.612319e+06 -# HELP go_memstats_frees_total Total number of frees. -# TYPE go_memstats_frees_total counter -go_memstats_frees_total 2.828263344e+09 -# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started. -# TYPE go_memstats_gc_cpu_fraction gauge -go_memstats_gc_cpu_fraction 0.03357798610671518 -# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. -# TYPE go_memstats_gc_sys_bytes gauge -go_memstats_gc_sys_bytes 1.3884192e+07 -``` -~~~ +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 -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 validators make and receive a lot of connections to other nodes. - -If you see errors such as: - -``` -Failed to accept incoming connection - Os { code: 24, kind: Other, message: "Too many open files" } -``` - -This means that the operating system is preventing network connections from being made. - -##### Set the ulimit via `systemd` service file -Query the `ulimit` of your validator with: - -``` -grep -i "open files" /proc/$(ps -A -o pid,cmd|grep nymd | grep -v grep |head -n 1 | awk '{print $1}')/limits -``` - -You'll get back the hard and soft limits, which looks something like this: - -``` -Max open files 65536 65536 files -``` - -If your output is **the same as above**, your node will not encounter any `ulimit` related issues. - -However if either value is `1024`, you must raise the limit via the systemd service file. Add the line: - -``` -LimitNOFILE=65536 -``` - -Reload the daemon: - -``` -systemctl daemon-reload -``` - -or execute this as root for system-wide setting of `ulimit`: - -``` -echo "DefaultLimitNOFILE=65535" >> /etc/systemd/system.conf -``` - -Reboot your machine and restart your node. When it comes back, use `cat /proc/$(pidof nym-validator)/limits | grep "Max open files"` to make sure the limit has changed to 65535. - -##### Set the ulimit on `non-systemd` based distributions -Edit `etc/security/conf` and add the following lines: - -``` -# Example hard limit for max opened files -username hard nofile 4096 -# Example soft limit for max opened files -username soft nofile 4096 -``` - -Then reboot your server and restart your validator. +Linux machines limit how many open files a user is allowed to have. This is called a `ulimit`. We need to set it to a higher value than the default 1024. Follow the instructions in the [maintenance page](./maintenance.md#Setting-the-ulimit) to change the `ulimit` value for validators. ## Using your validator -### Unjailing your validator +### Un-jailing your validator If your validator gets jailed, you can fix it with the following command: ``` @@ -593,19 +481,7 @@ nyxd tx slashing unjail ### Upgrading your validator -Upgrading from `v0.31.1` -> `v0.32.0` process is fairly simple. Grab the `v0.32.0` release tarball from the [`nyxd` releases page](https://github.com/nymtech/nyxd/releases), and untar it. Inside are two files: - -- the new validator (`nyxd`) v0.32.0 -- the new wasmvm (it depends on your platform, but most common filename is `libwasmvm.x86_64.so`) - -Wait for the upgrade height to be reached and the chain to halt awaiting upgrade, then: - -* copy `libwasmvm.x86_64.so` to the default LD_LIBRARY_PATH on your system (on Ubuntu 20.04 this is `/lib/x86_64-linux-gnu/`) replacing your existing file with the same name. -* swap in your new `nyxd` binary and restart. - -You can also use something like [Cosmovisor](https://github.com/cosmos/cosmos-sdk/tree/main/tools/cosmovisor) - grab the relevant information from the current upgrade proposal [here](https://nym.explorers.guru/proposal/9). - -Note: Cosmovisor will swap the `nyxd` binary, but you'll need to already have the `libwasmvm.x86_64.so` in place. +To upgrade your validator, follow the steps on the [maintenance page](./maintenance.md#setting-the-ulimit). #### Common reasons for your validator being jailed @@ -623,7 +499,7 @@ You can check your current balances with: nymd query bank balances ${ADDRESS} ``` -For example, on the Sanbox testnet this would return: +For example, on the Sandbox testnet this would return: ```yaml balances: @@ -659,11 +535,3 @@ nyxd tx staking delegate VALOPERADDRESS AMOUNTunymt --fees 5000unyxt ``` -## Validator port reference -All validator-specific port configuration can be found in `$HOME/.nymd/config/config.toml`. If you do edit any port configs, remember to restart your validator. - -| Default port | Use | -|--------------|--------------------------------------| -| 1317 | REST API server endpoint | -| 26656 | Listen for incoming peer connections | -| 26660 | Listen for Prometheus connections | diff --git a/documentation/operators/src/not-found.md b/documentation/operators/src/not-found.md new file mode 100644 index 0000000000..aafb70cba5 --- /dev/null +++ b/documentation/operators/src/not-found.md @@ -0,0 +1,11 @@ +# Page Not Found + +You seem to have followed a link to a page that no longer exists. Our documentation is changing and growing over time, so this page has most likely been moved somewhere else. + +For node setup guides, see the [Operator Guides book](https://nymtech.net/operators). + +For developer tutorials, app guides, and demo and community apps, see the [Developer Portal](https://nymtech.net/developers). + +If you are looking for information on setting up and maintaining infrastructure nodes, check the sitemap on the left hand side of the screen. + +If you still can't find what you're looking for get in touch with us via [Matrix](https://matrix.to/#/#nym-community:nymtech.chat). \ No newline at end of file diff --git a/documentation/operators/src/preliminary-steps.md b/documentation/operators/src/preliminary-steps.md new file mode 100644 index 0000000000..2f7cb2008b --- /dev/null +++ b/documentation/operators/src/preliminary-steps.md @@ -0,0 +1,41 @@ +# Preliminary Steps + +> The Nym `mixnode`, `gateway` and `network-requester` binaries were 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. + +There are a couple of steps that need completing before starting to set up your mix node, gateway or a network requester: + +- preparing your [desktop wallet](https://nymtech.net/docs/wallet/desktop-wallet.html) or [CLI wallet](https://nymtech.net/docs/wallet/cli-wallet.html). +- requisitioning a VPS (Virtual Private Server) + +### Wallet preparation +#### Mainnet +Before you initialise and run your mix node, head to our [website](https://nymtech.net/download/) and download the Nym wallet for your operating system. If pre-compiled binaries for your operating system aren't available, you can build the wallet yourself with instructions [here](https://nymtech.net/docs/wallet/desktop-wallet.html). + +If you don't already have one, please create a Nym address using the wallet, and fund it with tokens. The minimum amount required to bond a mix node is 100 `NYM`, but make sure you have a bit more to account for gas costs. + +`NYM` can be purchased via Bity from the wallet itself with BTC or fiat, and is currently present on several [exchanges](https://www.coingecko.com/en/coins/nym#markets). + +> Remember that you can **only** use Cosmos `NYM` tokens to bond your mix node. You **cannot** use ERC20 representations of `NYM` to run a node. + + +#### Sandbox testnet +Make sure to download a wallet and create an account as outlined above. Then head to our [token faucet](https://faucet.nymtech.net/) and get some tokens to use to bond it. + +### VPS Hardware Specs +You will need to rent a VPS to run your node on. One key reason for this is that your node **must be able to send TCP data using both IPv4 and IPv6** (as other nodes you talk to may use either protocol). + +For the moment, we haven't put a great amount of effort into optimizing concurrency to increase throughput, so don't bother provisioning a beastly server with multiple cores. This will change when we get a chance to start doing performance optimizations in a more serious way. Sphinx packet decryption is CPU-bound, so once we optimize, more fast cores will be better. + +For now, see the below rough specs: + +- Processors: 2 cores are fine. Get the fastest CPUs you can afford. + +#### For mix node + +- RAM: Memory requirements are very low - typically a mix node may use only a few hundred MB of RAM. +- Disks: The mixnodes require no disk space beyond a few bytes for the configuration files. + +#### For Gateway + +- RAM: Memory requirements depend on the amount of users your Gateway will be serving at any one time. If you're just going to be using it yourself, then minimal RAM is fine. **If you're running your Gateway as part of a Service Grant, get something with at least 4GB RAM.** +- Disks: much like the amount of RAM your Gateway could use, the amount of disk space required will vary with the amount of users your Gateway is serving. **If you're running your Gateway as part of a Service Grant, get something with at least 40GB storage.** diff --git a/documentation/operators/theme/css/chrome.css b/documentation/operators/theme/css/chrome.css new file mode 100644 index 0000000000..febd1d40a6 --- /dev/null +++ b/documentation/operators/theme/css/chrome.css @@ -0,0 +1,538 @@ +/* CSS for UI elements (a.k.a. chrome) */ + +@import 'variables.css'; + +::-webkit-scrollbar { + background: var(--bg); +} +::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} +html { + scrollbar-color: var(--scrollbar) var(--bg); +} +#searchresults a, +.content a:link, +a:visited, +a > .hljs { + color: var(--links); +} + +/* Menu Bar */ + +#menu-bar, +#menu-bar-hover-placeholder { + z-index: 101; + margin: auto calc(0px - var(--page-padding)); +} +#menu-bar { + position: relative; + display: flex; + flex-wrap: wrap; + background-color: var(--bg); + border-bottom-color: var(--bg); + border-bottom-width: 1px; + border-bottom-style: solid; +} +#menu-bar.sticky, +.js #menu-bar-hover-placeholder:hover + #menu-bar, +.js #menu-bar:hover, +.js.sidebar-visible #menu-bar { + position: -webkit-sticky; + position: sticky; + top: 0 !important; +} +#menu-bar-hover-placeholder { + position: sticky; + position: -webkit-sticky; + top: 0; + height: var(--menu-bar-height); +} +#menu-bar.bordered { + border-bottom-color: var(--table-border-color); +} +#menu-bar i, #menu-bar .icon-button { + position: relative; + padding: 0 8px; + z-index: 10; + line-height: var(--menu-bar-height); + cursor: pointer; + transition: color 0.5s; +} +@media only screen and (max-width: 420px) { + #menu-bar i, #menu-bar .icon-button { + padding: 0 5px; + } +} + +.icon-button { + border: none; + background: none; + padding: 0; + color: inherit; +} +.icon-button i { + margin: 0; +} + +.right-buttons { + margin: 0 15px; +} +.right-buttons a { + text-decoration: none; +} + +.left-buttons { + display: flex; + margin: 0 5px; +} +.no-js .left-buttons { + display: none; +} + +.menu-title { + display: inline-block; + font-weight: 200; + font-size: 2.4rem; + line-height: var(--menu-bar-height); + text-align: center; + margin: 0; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.js .menu-title { + cursor: pointer; +} + +.menu-bar, +.menu-bar:visited, +.nav-chapters, +.nav-chapters:visited, +.mobile-nav-chapters, +.mobile-nav-chapters:visited, +.menu-bar .icon-button, +.menu-bar a i { + color: var(--icons); +} + +.menu-bar i:hover, +.menu-bar .icon-button:hover, +.nav-chapters:hover, +.mobile-nav-chapters i:hover { + color: var(--icons-hover); +} + +/* Nav Icons */ + +.nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + + position: fixed; + top: 0; + bottom: 0; + margin: 0; + max-width: auto; + min-width: auto; + + display: flex; + justify-content: center; + align-content: center; + flex-direction: column; + + transition: color 0.5s, background-color 0.5s; +} + +.nav-chapters:hover { + text-decoration: none; + background-color: var(--theme-hover); + transition: background-color 0.15s, color 0.15s; +} + +.nav-wrapper { + margin-top: 50px; + display: none; +} + +.mobile-nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + width: 90px; + border-radius: 5px; + background-color: var(--sidebar-bg); +} + +.previous { + float: left; +} + +.next { + float: right; + right: var(--page-padding); +} + +@media only screen and (max-width: 1080px) { + .nav-wide-wrapper { display: none; } + .nav-wrapper { display: block; } +} + +@media only screen and (max-width: 1380px) { + .sidebar-visible .nav-wide-wrapper { display: none; } + .sidebar-visible .nav-wrapper { display: block; } +} + +/* Inline code */ + +:not(pre) > .hljs { + display: inline; + padding: 0.1em 0.3em; + border-radius: 3px; +} + +:not(pre):not(a) > .hljs { + color: var(--inline-code-color); + overflow-x: initial; +} + +a:hover > .hljs { + text-decoration: underline; +} + +pre { + position: relative; +} +pre > .buttons { + position: absolute; + z-index: 100; + right: 0px; + top: 2px; + margin: 0px; + padding: 2px 0px; + + color: var(--sidebar-fg); + cursor: pointer; + visibility: hidden; + opacity: 0; + transition: visibility 0.1s linear, opacity 0.1s linear; +} +pre:hover > .buttons { + visibility: visible; + opacity: 1 +} +pre > .buttons :hover { + color: var(--sidebar-active); + border-color: var(--icons-hover); + background-color: var(--theme-hover); +} +pre > .buttons i { + margin-left: 8px; +} +pre > .buttons button { + cursor: inherit; + margin: 0px 5px; + padding: 3px 5px; + font-size: 14px; + + border-style: solid; + border-width: 1px; + border-radius: 4px; + border-color: var(--icons); + background-color: var(--theme-popup-bg); + transition: 100ms; + transition-property: color,border-color,background-color; + color: var(--icons); +} +@media (pointer: coarse) { + pre > .buttons button { + /* On mobile, make it easier to tap buttons. */ + padding: 0.3rem 1rem; + } +} +pre > code { + padding: 1rem; +} + +/* FIXME: ACE editors overlap their buttons because ACE does absolute + positioning within the code block which breaks padding. The only solution I + can think of is to move the padding to the outer pre tag (or insert a div + wrapper), but that would require fixing a whole bunch of CSS rules. +*/ +.hljs.ace_editor { + padding: 0rem 0rem; +} + +pre > .result { + margin-top: 10px; +} + +/* Search */ + +#searchresults a { + text-decoration: none; +} + +mark { + border-radius: 2px; + padding: 0 3px 1px 3px; + margin: 0 -3px -1px -3px; + background-color: var(--search-mark-bg); + transition: background-color 300ms linear; + cursor: pointer; +} + +mark.fade-out { + background-color: rgba(0,0,0,0) !important; + cursor: auto; +} + +.searchbar-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} + +#searchbar { + width: 100%; + margin: 5px auto 0px auto; + padding: 10px 16px; + transition: box-shadow 300ms ease-in-out; + border: 1px solid var(--searchbar-border-color); + border-radius: 3px; + background-color: var(--searchbar-bg); + color: var(--searchbar-fg); +} +#searchbar:focus, +#searchbar.active { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +.searchresults-header { + font-weight: bold; + font-size: 1em; + padding: 18px 0 0 5px; + color: var(--searchresults-header-fg); +} + +.searchresults-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); + border-bottom: 1px dashed var(--searchresults-border-color); +} + +ul#searchresults { + list-style: none; + padding-left: 20px; +} +ul#searchresults li { + margin: 10px 0px; + padding: 2px; + border-radius: 2px; +} +ul#searchresults li.focus { + background-color: var(--searchresults-li-bg); +} +ul#searchresults span.teaser { + display: block; + clear: both; + margin: 5px 0 0 20px; + font-size: 0.8em; +} +ul#searchresults span.teaser em { + font-weight: bold; + font-style: normal; +} + +/* Sidebar */ + +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: var(--sidebar-width); + font-size: 1em; + box-sizing: border-box; + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.sidebar-resizing { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +.js:not(.sidebar-resizing) .sidebar { + transition: transform 0.3s; /* Animation: slide away */ +} +.sidebar code { + line-height: 2em; +} +.sidebar .sidebar-scrollbox { + overflow-y: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + padding: 10px 10px; +} +.sidebar .sidebar-resize-handle { + position: absolute; + cursor: col-resize; + width: 0; + right: 0; + top: 0; + bottom: 0; +} +.js .sidebar .sidebar-resize-handle { + cursor: col-resize; + width: 5px; +} +.sidebar-hidden .sidebar { + transform: translateX(calc(0px - var(--sidebar-width))); +} +.sidebar::-webkit-scrollbar { + background: var(--sidebar-bg); +} +.sidebar::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} + +.sidebar-visible .page-wrapper { + transform: translateX(var(--sidebar-width)); +} +@media only screen and (min-width: 620px) { + .sidebar-visible .page-wrapper { + transform: none; + margin-left: var(--sidebar-width); + } +} + +.chapter { + list-style: none outside none; + padding-left: 0; + line-height: 2em; +} + +.chapter ol { + width: 100%; +} + +.chapter li { + display: flex; + color: var(--sidebar-non-existant); +} +.chapter li a { + display: block; + padding: 0; + text-decoration: none; + color: var(--sidebar-fg); +} + +.chapter li a:hover { + color: var(--sidebar-active); +} + +.chapter li a.active { + color: var(--sidebar-active); +} + +.chapter li > a.toggle { + cursor: pointer; + display: block; + margin-left: auto; + padding: 0 10px; + user-select: none; + opacity: 0.68; +} + +.chapter li > a.toggle div { + transition: transform 0.5s; +} + +/* collapse the section */ +.chapter li:not(.expanded) + li > ol { + display: none; +} + +.chapter li.chapter-item { + line-height: 1.5em; + margin-top: 0.6em; +} + +.chapter li.expanded > a.toggle div { + transform: rotate(90deg); +} + +.spacer { + width: 100%; + height: 3px; + margin: 5px 0px; +} +.chapter .spacer { + background-color: var(--sidebar-spacer); +} + +@media (-moz-touch-enabled: 1), (pointer: coarse) { + .chapter li a { padding: 5px 0; } + .spacer { margin: 10px 0; } +} + +.section { + list-style: none outside none; + padding-left: 20px; + line-height: 1.5em; +} + +/* Theme Menu Popup */ + +.theme-popup { + position: absolute; + left: 10px; + top: var(--menu-bar-height); + z-index: 1000; + border-radius: 4px; + font-size: 0.7em; + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); + margin: 0; + padding: 0; + list-style: none; + display: none; + /* Don't let the children's background extend past the rounded corners. */ + overflow: hidden; +} +.theme-popup .default { + color: var(--icons); +} +.theme-popup .theme { + width: 100%; + border: 0; + margin: 0; + padding: 2px 20px; + line-height: 25px; + white-space: nowrap; + text-align: left; + cursor: pointer; + color: inherit; + background: inherit; + font-size: inherit; +} +.theme-popup .theme:hover { + background-color: var(--theme-hover); +} + +.theme-selected::before { + display: inline-block; + content: "✓"; + margin-left: -14px; + width: 14px; +} diff --git a/documentation/operators/theme/css/general.css b/documentation/operators/theme/css/general.css new file mode 100644 index 0000000000..a06db43965 --- /dev/null +++ b/documentation/operators/theme/css/general.css @@ -0,0 +1,203 @@ +/* Base styles and content styles */ + +@import 'variables.css'; + +:root { + /* Browser default font-size is 16px, this way 1 rem = 10px */ + font-size: 70%; +} + +html { + font-family: "Open Sans", sans-serif; + color: var(--fg); + background-color: var(--bg); + text-size-adjust: none; + -webkit-text-size-adjust: none; +} + +body { + margin: 0; + font-size: 1.5rem; + overflow-x: hidden; +} + +code { + font-family: var(--mono-font) !important; + font-size: 0.9em; +} + +/* make long words/inline code not x overflow */ +main { + overflow-wrap: break-word; +} + +/* make wide tables scroll if they overflow */ +.table-wrapper { + overflow-x: auto; +} + +/* Don't change font size in headers. */ +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + font-size: unset; +} + +.left { float: left; } +.right { float: right; } +.boring { opacity: 0.6; } +.hide-boring .boring { display: none; } +.hidden { display: none !important; } + +h2, h3 { margin-top: 2.5em; } +h4, h5 { margin-top: 2em; } + +.header + .header h3, +.header + .header h4, +.header + .header h5 { + margin-top: 1em; +} + +h1:target::before, +h2:target::before, +h3:target::before, +h4:target::before, +h5:target::before, +h6:target::before { + display: inline-block; + content: "»"; + margin-left: -30px; + width: 30px; +} + +/* This is broken on Safari as of version 14, but is fixed + in Safari Technology Preview 117 which I think will be Safari 14.2. + https://bugs.webkit.org/show_bug.cgi?id=218076 +*/ +:target { + scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); +} + +.page { + outline: 0; + padding: 0 var(--page-padding); + margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ +} +.page-wrapper { + box-sizing: border-box; +} +.js:not(.sidebar-resizing) .page-wrapper { + transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ +} + +.content { + overflow-y: auto; + padding: 0 10px; +} +.content main { + margin-left: 10%; + margin-right: 10%; + max-width: var(--content-max-width); +} +.content p { line-height: 1.45em; } +.content ol { line-height: 1.45em; } +.content ul { line-height: 1.45em; } +.content a { text-decoration: none; } +.content a:hover { text-decoration: underline; } +.content img, .content video { max-width: 100%; } +.content .header:link, +.content .header:visited { + color: var(--fg); +} +.content .header:link, +.content .header:visited:hover { + text-decoration: none; +} + +table { + margin: 0 auto; + border-collapse: collapse; +} +table td { + padding: 3px 20px; + border: 1px var(--table-border-color) solid; +} +table thead { + background: var(--table-header-bg); +} +table thead td { + font-weight: 700; + border: none; +} +table thead th { + padding: 3px 20px; +} +table thead tr { + border: 1px var(--table-header-bg) solid; +} +/* Alternate background colors for rows */ +table tbody tr:nth-child(2n) { + background: var(--table-alternate-bg); +} + + +blockquote { + margin: 20px 0; + padding: 0 20px; + color: var(--fg); + background-color: var(--quote-bg); + border-top: .1em solid var(--quote-border); + border-bottom: .1em solid var(--quote-border); +} + +kbd { + background-color: var(--table-border-color); + border-radius: 4px; + border: solid 1px var(--theme-popup-border); + box-shadow: inset 0 -1px 0 var(--theme-hover); + display: inline-block; + font-size: var(--code-font-size); + font-family: var(--mono-font); + line-height: 10px; + padding: 4px 5px; + vertical-align: middle; +} + +:not(.footnote-definition) + .footnote-definition, +.footnote-definition + :not(.footnote-definition) { + margin-top: 2em; +} +.footnote-definition { + font-size: 0.9em; + margin: 0.5em 0; +} +.footnote-definition p { + display: inline; +} + +.tooltiptext { + position: absolute; + visibility: hidden; + color: #fff; + background-color: #333; + transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ + left: -8px; /* Half of the width of the icon */ + top: -35px; + font-size: 0.8em; + text-align: center; + border-radius: 6px; + padding: 5px 8px; + margin: 5px; + z-index: 1000; +} +.tooltipped .tooltiptext { + visibility: visible; +} + +.chapter li.part-title { + color: var(--sidebar-fg); + margin: 5px 0px; + font-weight: bold; +} + +.result-no-output { + font-style: italic; +} diff --git a/documentation/operators/theme/css/variables.css b/documentation/operators/theme/css/variables.css new file mode 100644 index 0000000000..8dfcf92239 --- /dev/null +++ b/documentation/operators/theme/css/variables.css @@ -0,0 +1,3257 @@ + +/* Globals */ + +:root { + --sidebar-width: 300px; + --page-padding: 20px; + --content-max-width: 70%; + --menu-bar-height: 40px; + --mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; + --code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */ + --pagetoc-width: 13%; + --pagetoc-fontsize: 14.5px; +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +/* Themes */ + +.ayu { + --bg: hsl(210, 25%, 8%); + --fg: #c5c5c5; + + --sidebar-bg: #14191f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #5c6773; + --sidebar-active: #ffb454; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #0096cf; + + --inline-code-color: #ffb454; + + --theme-popup-bg: #14191f; + --theme-popup-border: #5c6773; + --theme-hover: #191f26; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(210, 25%, 13%); + --table-header-bg: hsl(210, 25%, 28%); + --table-alternate-bg: hsl(210, 25%, 11%); + + --searchbar-border-color: #848484; + --searchbar-bg: #424242; + --searchbar-fg: #fff; + --searchbar-shadow-color: #d4c89f; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #252932; + --search-mark-bg: #e3b171; +} + +.coal { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; +} + +.light { + --bg: hsl(0, 0%, 100%); + --fg: hsl(0, 0%, 0%); + + --sidebar-bg: #fafafa; + --sidebar-fg: hsl(0, 0%, 0%); + --sidebar-non-existant: #aaaaaa; + --sidebar-active: #1f1fff; + --sidebar-spacer: #f4f4f4; + + --scrollbar: #8F8F8F; + + --icons: #747474; + --icons-hover: #000000; + + --links: #1f1fff; + + --inline-code-color: #F42C4C; + + --theme-popup-bg: #fafafa; + --theme-popup-border: #cccccc; + --theme-hover: #e6e6e6; + + --quote-bg: hsl(197, 37%, 96%); + --quote-border: hsl(197, 37%, 91%); + + --table-border-color: hsl(0, 0%, 95%); + --table-header-bg: hsl(0, 0%, 80%); + --table-alternate-bg: hsl(0, 0%, 97%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #e4f2fe; + --search-mark-bg: #a2cff5; +} + +.navy { + --bg: hsl(226, 23%, 11%); + --fg: #bcbdd0; + + --sidebar-bg: #282d3f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505274; + --sidebar-active: #2b79a2; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #161923; + --theme-popup-border: #737480; + --theme-hover: #282e40; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(226, 23%, 16%); + --table-header-bg: hsl(226, 23%, 31%); + --table-alternate-bg: hsl(226, 23%, 14%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #aeaec6; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #5f5f71; + --searchresults-border-color: #5c5c68; + --searchresults-li-bg: #242430; + --search-mark-bg: #a2cff5; +} + +.rust { + --bg: hsl(60, 9%, 87%); + --fg: #262625; + + --sidebar-bg: #3b2e2a; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505254; + --sidebar-active: #e69f67; + --sidebar-spacer: #45373a; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #262625; + + --links: #2b79a2; + + --inline-code-color: #6e6b5e; + + --theme-popup-bg: #e1e1db; + --theme-popup-border: #b38f6b; + --theme-hover: #99908a; + + --quote-bg: hsl(60, 5%, 75%); + --quote-border: hsl(60, 5%, 70%); + + --table-border-color: hsl(60, 9%, 82%); + --table-header-bg: #b3a497; + --table-alternate-bg: hsl(60, 9%, 84%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #dec2a2; + --search-mark-bg: #e69f67; +} + +@media (prefers-color-scheme: dark) { + .light.no-js { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; + } +} diff --git a/documentation/operators/theme/index.hbs b/documentation/operators/theme/index.hbs new file mode 100644 index 0000000000..06a3731f33 --- /dev/null +++ b/documentation/operators/theme/index.hbs @@ -0,0 +1,1063 @@ + + + + + + {{ title }} + {{#if is_print }} + + {{/if}} + {{#if base_url}} + + {{/if}} + + + + {{> head}} + + + + + + {{#if favicon_svg}} + + {{/if}} + {{#if favicon_png}} + + {{/if}} + + + + {{#if print_enable}} + + {{/if}} + + + + {{#if copy_fonts}} + + {{/if}} + + + + + + + + {{#each additional_css}} + + {{/each}} + + {{#if mathjax_support}} + + + {{/if}} + + + + + + + + + + + + + + + + +
+ +
+ {{> header}} + + + + {{#if search_enabled}} + + {{/if}} + + + + +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + {{{ content }}} +
+ + +
+
+ + + +
+ + {{#if live_reload_endpoint}} + + + {{/if}} + + {{#if google_analytics}} + + + {{/if}} + + {{#if playground_line_numbers}} + + {{/if}} + + {{#if playground_copyable}} + + {{/if}} + + {{#if playground_js}} + + + + + + {{/if}} + + {{#if search_js}} + + + + {{/if}} + + + + + + + {{#each additional_js}} + + {{/each}} + + {{#if is_print}} + {{#if mathjax_support}} + + {{else}} + + {{/if}} + {{/if}} + + + diff --git a/documentation/operators/theme/pagetoc.css b/documentation/operators/theme/pagetoc.css new file mode 100644 index 0000000000..a68e5b8e21 --- /dev/null +++ b/documentation/operators/theme/pagetoc.css @@ -0,0 +1,47 @@ +/* src: https://github.com/JorelAli/mdBook-pagetoc */ + +@media only screen and (max-width:1439px) { + .sidetoc { + display: none; + } +} + +@media only screen and (min-width:1440px) { + main { + position: relative; + } + .sidetoc { + margin-left: auto; + margin-right: auto; + /* left: calc(90% + (var(--content-min-width))/4 - 110px); */ + left: 101%; + position: absolute; + font-size: var(--pagetoc-fontsize); + } + .pagetoc { + position: fixed; + width: var(--pagetoc-width); + } + .pagetoc a { + border-left: 1px solid var(--sidebar-bg); + /* color: var(--fg); */ + /* color: var(--sidebar-fg); */ + color: var(--links); + display: block; + padding-bottom: 5px; + padding-top: 5px; + padding-left: 10px; + text-align: left; + text-decoration: none; + font-weight: normal; + background: var(--sidebar-bg); + } + .pagetoc a:hover, + .pagetoc a.active { + background: var(--sidebar-bg); + /* color: var(--sidebar-fg); */ + color: var(--sidebar-active); + font-weight: bold; + font-size: var(--pagetoc-fontsize); + } +} diff --git a/documentation/operators/theme/pagetoc.js b/documentation/operators/theme/pagetoc.js new file mode 100644 index 0000000000..b11052427e --- /dev/null +++ b/documentation/operators/theme/pagetoc.js @@ -0,0 +1,68 @@ +// src: https://github.com/JorelAli/mdBook-pagetoc + +// Un-active everything when you click it +Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el, i) { + el.addEventHandler("click", function() { + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el, i) { + el.classList.remove("active"); + }); + el.classList.add("active"); + }); +}); + +var updateFunction = function() { + + var id; + var elements = document.getElementsByClassName("header"); + Array.prototype.forEach.call(elements, function(el, i) { + if (window.pageYOffset >= el.offsetTop) { + id = el; + } + }); + + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el, i) { + el.classList.remove("active"); + }); + + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el, i) { + if (id.href.localeCompare(el.href) == 0) { + el.classList.add("active"); + } + }); +}; + +// Populate sidebar on load +window.addEventListener('load', function() { + var pagetoc = document.getElementsByClassName("pagetoc")[0]; + var elements = document.getElementsByClassName("header"); + Array.prototype.forEach.call(elements, function(el, i) { + var link = document.createElement("a"); + + // Indent shows hierarchy + var indent = ""; + switch (el.parentElement.tagName) { + case "H2": + indent = "20px"; + break; + case "H3": + indent = "40px"; + break; + case "H4": + indent = "60px"; + break; + default: + break; + } + + link.appendChild(document.createTextNode(el.text)); + link.style.paddingLeft = indent; + link.href = el.href; + pagetoc.appendChild(link); + }); + updateFunction.call(); +}); + + + +// Handle active elements on scroll +window.addEventListener("scroll", updateFunction);