Introduction

Welcome to the Nym Developer Portal, containing quickstart resources, user manuals, integration information, and tutorials outlining to start building privacy enhanced apps.

For more in-depth information about nodes, network traffic flows, clients, coconut etc check out the docs. 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 book.

Last change: 2023-09-19, commit: 8aa8f07c

What is Nym?

Nym is a privacy platform that secures user data and protects against surveillance. It leverages the mixnet, a type of overlay network that makes both content and metadata of transactions private through network-level obfuscation and incentivisation, and Coconut, a privacy technology that creates an application-level private access control layer. Nym also utilises Nyx, our Cosmos SDK blockchain, to allow for us to use payment tokens in the form of NYM, as well as smart contracts, in order to create a robust, decentralised, and secure environment for the mixnet. In a nutshell, Nym is a solution that provides strong privacy protection for users in the digital world.

All the technical nuances about Nym platform can come out quite complex at first. Look for any questions in the FAQ section, take part in our DevRel AMAs or seek support in Nym’s community

An overlay network for network-level traffic privacy

Our mixnet design is based on the Loopix Anonymity System, somewhat modified to provide better quality of service guarantees.

In this brief video, our Head of Research and creator of the Loopix mixnet paper, Ania Piotrowska, delves into the design of the Loopix mixnet in depth at USENix 2017.

The Nym mixnet effectively addresses privacy concerns by utilizing network nodes to mix messages, making them unidentifiable to adversaries. Each packet is layer encrypted, and binary-padded so that it’s indistinguishable from all other packets. Incoming packets are “mixed” with all other messages inside the node. That is, the node strips one layer of packet encryption, and adds a small random transmission delay, so that messages are not emitted in the same order as which they arrived.

Next, the message is sent to another mix node, decrypted and mixed again, then to a third mixnode for further mixing. Finally, the message is delivered to its destination gateway.

As long as there’s enough traffic flowing through the nodes, even an adversary who can monitor the entire network cannot trace the packet flow through the system.

Privacy Enhanced Applications (PEApps) that need to defend against network-level monitoring can utilize the Nym mixnet to protect against network-level surveillance.

Revolutionising Privacy: An Incentivized Mixnet, the First of its Kind

Nym is the first mixnet to incentivise its node operators via a cryptocurrency: the NYM token. The tokenomic design of Nym ensures that nodes are motivated to provide top-notch performance and robust security, as they are financially rewarded for doing so. The video below contains an explanation of Nym’s tokenomic design from Nym’s Chief Scientist Claudia Diaz:

ELI5 Understanding of the Implications of Nym

Nym is a type of technology that is designed to keep your online activities private from surveillance networks which track users of many online services. As well as protecting the contents of your messages - the data - Nym’s design means that metadata - data about the data - is also kept private. This means that the location from which you sent your message, the time you did so, and a lot of information about your network connection is kept private: this is information that can be used to deanonymise users of other privacy systems, even if they employ encrypted messaging!

Nym works by “mixing” your data with other users’ data, making it much more difficult for anyone to single out and track just your information.

This system is unique because it provides an incentive for people to participate and help improve the network, making it stronger and more secure for everyone. By breaking down Nym and understanding its implications, you can see why it’s an important tool for anyone who values their privacy online.

Last change: 2023-09-19, commit: 8aa8f07c

Nym vs Other Systems

Why use Nym: Understanding the Significance of Nym

Nym is the first system we’re aware of which provides integrated protection on both the network and transaction level at once. This seamless approach gives the best possible privacy protections, ensuring that nothing falls through the cracks between systems.

The diagram and brief explainer texts below give a high level overview of the difference between Nym and other comparable systems.

If you want to dig more deeply into the way traffic is packetised and moved through the mixnet, check out the Mixnet Traffic Flow page of the docs.

Nym vs VPNs

The most popular network-level privacy solution currently is the VPN (virtual private network), which provides network-level protection via an encrypted tunnel between a user’s computer and one run by a VPN provider. VPNs are often misconfigured, however, and even when configured correctly, don’t offer real privacy or adequate resistance to censorship.

VPN providers can also fully observe all network traffic between users and the public internet, knowing exactly what services its users are accessing at a given time. The user must trust that the VPN provider is not using their information in a malicious manner or keeping logs.

The Nym mixnet is an anonymous overlay network that provides strong network-level anonymity, even in the face of powerful systems capable of passively monitoring the entire network. The mixnet is decentralized, with no trusted third parties, and so does not require a trusted provider like a VPN. More importantly, Nym provides superior privacy to VPNs and can support high-quality of service and low latency through incentives.

Nym vs Tor

Tor is the best-known anonymous overlay network today. Unlike VPNs, Tor provides a ‘circuit’ of three hops that provides better privacy than single-node VPNs, so any single node in Tor can’t deanonymize traffic. Tor’s onion-routing encrypts traffic between each hop so that only the final hop, the Tor ‘exit node’, can decrypt the package.

However, Tor’s anonymity properties can be defeated by an entity that is capable of monitoring the entire network’s ‘entry’ and ‘exit’ nodes, because while onion-routing encrypts traffic, Tor does not add timing obfuscation or use decoy traffic to obfuscate the traffic patterns which can be used to deanonymize users. Although these kinds of attacks were thought to be unrealistic when Tor was invented, in the era of powerful government agencies and private companies, these kinds of attacks are a real threat. Tor’s design is also based on a centralized directory authority for routing.

While Tor may be the best existing solution for general-purpose web-browsing that accesses the entire internet, it is inarguable that mixnets are better than Tor for message-passing systems such as cryptocurrency transactions and secure messaging, and we believe well designed incentives can also enable the use of Nym as a general purpose decentralized VPN. The Nym mixnet provides superior privacy by making packets indistinguishable from each other, adding cover traffic, and providing timing obfuscation. Unlike both previous mixnet designs and Tor, the Nym mixnet decentralizes its shared operations using blockchain technology and uses incentives to both scale and provide censorship-resistance.

Nym vs I2P

I2P (‘Invisible Internet Project’) replaces Tor’s directory authority with a distributed hash table for routing. How to design a secure and private distributed hash table is still an open research question, and I2P is open to a number of attacks that isolate, misdirect, or deanonymize users. Like Tor, I2P is based on ‘security by obscurity’, where it is assumed that no adversary can watch the entire network. While security by obscurity may have been cutting-edge at the turn of the millennium, such an approach is rapidly showing its age.

Nym’s cutting-edge mixnet design guarantees network anonymity and resistance to surveillance even in the face of powerful deanonymizing attacks. Unlike I2P, Nym adds decoy traffic and timing obfuscation. Rather than a centralized directory authority or distributed hash table, Nym uses blockchain technology and economic incentives to decentralize its network.The Nym mixnet can anonymize metadata even against government agencies or private companies who can monitor network links and observe the incoming and outgoing traffic of all clients and servers.

Nym vs Facebook Connect

The Nym credential system decentralizes the functions of systems like Facebook Connect while adding privacy. Personal data has become a toxic asset, even to companies who base their entire business around it, as evidenced by the hack of Facebook’s OAuth identity system in 2018 and the subsequent release of the data of 50 million users.

Unlike Facebook Connect and similar OAuth-based services like Sign in with Google, traditional usernames and passwords, or even public/private key pairs, Nym credentials allow users to authenticate and authorize data sharing without unwillingly revealing any information to a third party. There is no central third party in charge of the credentials, and users remain totally in control of their own data, disclosing it only to those who they want to. A user can store their data wherever they want (including on their own devices), and unlike alternatives like W3C’s DIDs, a user does not store anything on the blockchain, offering better privacy.

Last change: 2023-09-19, commit: 8aa8f07c

Node Types

Discover the workings of Nym’s privacy-enhancing mixnet infrastructure through the below video, where we break down the different types of nodes and how they each play a crucial role in ensuring secure and anonymous communication.

Mixnet Infrastructure

There are few types of Nym infrastructure nodes:

Mix Nodes

Mix nodes play a critical role in the Nym network by providing enhanced security and privacy to network content and metadata. They are part of the three-layer mixnet that ensures that communication remains anonymous and untraceable. Mix nodes receive NYM tokens as compensation for their quality of service, which is measured by the network validators.

Mix nodes anonymously relay encrypted Sphinx packets between each other, adding an extra layer of protection by reordering and delaying the packets before forwarding them to the intended recipient. Additionally, cover traffic is maintained through mix nodes sending Sphinx packets to other mix nodes, making it appear as if there is a constant flow of user messages and further protecting the privacy of legitimate data packets.

With the ability to hide, reorder and add a delay to network traffic, mix nodes make it difficult for attackers to perform time-based correlation attacks and deanonymize users. By consistently delivering high-quality service, mix nodes are rewarded with NYM tokens, reinforcing the integrity of the Nym network.

Gateways

Gateways serve as the point of entry for user data into the mixnet, verifying that users have acquired sufficient NYM-based bandwidth credentials before allowing encrypted packets to be forwarded to mixnodes. They are also responsible for safeguarding against denial of service attacks and act as a message storage for users who may go offline.

Gateways receive bandwidth credentials from users, which are periodically redeemed for NYM tokens as payment for their services. Users have the flexibility to choose a single gateway, split traffic across multiple gateways, run their own gateways, or a combination of these options.

In addition, gateways also cache messages, functioning as an inbox for users who are offline. By providing secure, reliable access to the mixnet and ensuring that data remains protected, gateways play a crucial role in maintaining the integrity of the Nym network.

Validators

Validators are essential to the security and integrity of the Nym network, tasked with several key responsibilities. They utilize proof-of-stake Sybil defense measures to secure the network and determine which nodes are included within it. Through their collaborative efforts, validators create Coconut threshold credentials which provide anonymous access to network data and resources.

Validators also play a critical role in maintaining the Nym Cosmos blockchain, a secure, public ledger that records network-wide information such as node public information and keys, network configuration parameters, CosmWasm smart contracts, and NYM and credential transactions.

Service Providers

Service Providers are a crucial aspect of the Nym infrastructure that support the application layer of the Nym network. Any application built with Nym will require a Service Provider, which can be created by anyone. Service Providers run a piece of binary code that enables them to handle requests from Nym users or other services, and then make requests to external servers on behalf of the users.

For example, a Service Provider could receive a request to check a mail server and then forward the response to the user. The presence of Service Providers in the Nym network enhances its security and privacy, making it a reliable and robust platform for anonymous communication and data exchange.

Where do I go from here? 💭

Maybe you would like to concentrate on building a application that uses the mixnet:

  • Explore the Tutorials section of the Developer Portal. Our in-depth tutorial on Building a Simple Service Provider give a good understanding of building User Clients and Service Providers in TypeScript, and how to configure Nym Websocket Clients for seamless communication with the mixnet.

  • Get started with using the Nym Mixnet quickly and easily by exploring the Quickstart options, such a NymConnect, proxying traffic through the Nym Socks5 client, or dive into integrating Nym into your existing application with the Integrations section.

Or perhaps you a developer that would like to run a infrastructure node such as a Gateway, Mix node or Network Requestor:

  • Check out the Network Overview docs page.

  • Take a look at our Node Setup Guide with our Nym Docs, containing setup guides for setting up you own infrastructure node.

Last change: 2023-09-19, commit: 8aa8f07c

Overview

There are multiple options to quickly connect to Nym and play with both the mixnet and credentials on the Sandbox testnet.

At most, these involve running Nym as a second process alongside an existing application in order to send traffic through the mixnet, most are either interact webpages or a standalone app.

If you’ve already covered the information in this section, or want to jump straight into integrating/ a Nym connection into an existing application, head to the Integrations section.

Last change: 2023-09-19, commit: 8aa8f07c

Chat demo (webapp)

You can find a browser-based ‘hello world’ chat app here.

Either open in two browser windows and send messages to yourself, or share with a friend and send messages to each other through the mixnet.

Last change: 2023-09-19, commit: 8aa8f07c

Coconut Credential Playground (webapp)

There is a coconut-scheme based Credential Library playground here. This is a WASM implementation of our Coconut libraries which generate raw Coconut credentials. Test it to create and re-randomize your own credentials.

For more information on what is happening here check out the Coconut docs.

Last change: 2023-09-19, commit: 8aa8f07c

SOCKS Proxy (CLI)

The socks5 client now also supports SOCKS4 and SOCKS4A protocols as well as SOCKS5.

The Nym socks5 client allows you to proxy traffic from a desktop application through the mixnet, meaning you can send and receive information from remote application servers without leaking metadata which can be used to deanonymise you, even if you’re using an encrypted application such as Signal.

Download or compile socks5 client

If you are using OSX or a Debian-based operating system, you can download the nym-socks5-client binary from our Github releases page.

If you are using a different operating system, head over to the Building from Source page for instructions on how to build the repository from source.

Initialise your socks5 client

Use the following command to initialise your socks5 client with the address of a Nym-operated Network Requester as a provider (the endpoint that will be proxying your traffic out of the mixnet) for ease:

./nym-socks5-client init --id quickstart --provider Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf

You can always check out the list of endpoints you can use otherwise on the mixnet service provider explorer page.

Start your socks5 client

Now your client is initialised, start it with the following:

./nym-socks5-client run --id quickstart

Proxying traffic

After completing the steps above, your local socks5 Client will be listening on localhost:1080 ready to proxy traffic to the Network Requester set as the --provider when initialising.

When trying to connect your app, generally the proxy settings are found in settings->advanced or settings->connection.

Here is an example of setting the proxy connecting in Blockstream Green:

Blockstream Green settings

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.

Further reading

If you want to dig more into the architecture and use of the socks5 client check out its documentation here.

Last change: 2023-09-19, commit: 8aa8f07c

NymConnect Beta (GUI)

NymConnect is a one-button GUI application that wraps around the nym-socks5-client for proxying application traffic through the Mixnet.

You can watch our video on getting started with NymConnect:

Install NymConnect and select an application that you want to privacy-enhance from the dropdown menu. For now, NymConnect can be used with Electrum Wallet, Keybase, desktop Telegram and Blockstream Green. Configure these to run via a SOCKS5 proxy and send their data through the Nym mixnet!

Please note that NymConnect is currently released in beta. Please report bugs via Github.

Usage instuctions

  • Download and install NymConnect.
  • Select your service provider from the dropdown menu.
  • Click connect - NymConnect will connect to a service provider and its SOCKS Proxy (IP) and Port will be displayed.
  • Click on IP or Port to copy their values to the clipboard.
  • Go to your app settings and look for the network/proxy settings. Select running via SOCKS5 proxy and paste the IP and Port values given by NymConnect.

Your traffic from that application will now run through the mixnet for privacy and unlinkability!

Last change: 2023-09-19, commit: 8aa8f07c

Monero NymConnect Integration

New Nym mixnet integration launched for Monero desktop to secure the right to financial privacy and censorship-resistance

Financial privacy is an important component of digital currencies and the use of Nym will provide users with the highest level of privacy at the infrastructure level. All users of digital currencies should be afforded equal rights to protection from financial surveillance.

A team made up of Monero community members have successfully set up a service provider to use Monero (using the Monero desktop wallet) over the Nym mixnet. This allows Monero users to easily use NymConnect to run Monero over the mixnet, thereby enhancing the privacy of Monero transactions.

How can I use Monero over the Nym mixnet?

Any syntax in <> brackets is a user’s unique variable. Exchange with a corresponding name without the <> brackets.

The mainnet service provider to Monero over the Nym mixnet is now ready for use via NymConnect.

  • Download the latest version of NymConnect.
  • Make sure your NymConnect is executable.
# in Linux open terminal in the same folder and run:
chmod +x ./nym-connect_<YOUR_VERSION>.AppImage
  • Open NymConnect app
  • Turn it on - Monero wallet is listed in the apps supported by default, no need for any setup
  • Copy the Socks5 address and Port

Then go to your Monero wallet (desktop or CLI) and change the settings to run over socks5 proxy:

Monero desktop wallet:

  • Settings -> Interface -> Socks5 proxy -> Add values: IP address 127.0.0.1, Port 1080 (the values copied from NymConnect)

CLI wallet

  • Monerod: add --proxy 127.0.0.1:1080 --bootstrap-daemon-proxy 127.0.0.1:1080 to args

  • Monero-wallet-{rpc, cli}: add --proxy 127.0.0.1:1080 --daemon-ssl-allow-any-cert to args

For those who want to try it out in testnet, a stagenet service provider is also available: https://nymtech.net/.wellknown/connect/service-providers.json

Now your Monero traffic is protected by the network privacy of Nym Mixnet.

Last change: 2023-09-19, commit: 8aa8f07c

Matrix NymConnect Integration

Chat applications became an essential part of human communication. Matrix chat has end to end encryption on protocol level and Element app users can sort their communication into spaces and rooms. Now the Matrix communities can rely on network privacy as NymConnect supports Matrix chat protocol.

Currently there is no option in Matrix’s Element client to set a Socks5 proxy. In order to use Element via NymConnect users have to start it from the command-line. The setup is simple, for convenience a keyboard shortcut setting or terminal alias can be easily done.

Setup & Run

Make sure you have installed and started NymConnect on your desktop.

To then start Matrix’s Element client via a Socks5 proxy connected to NymConnect, open terminal and run:

element-desktop --proxy-server=socks5://127.0.0.1:1080

Optimise setup with a keybinding / alias

Keybinding

An eloquent solution to avoid entering a command every time is to setup your keybinding. Open your settings, navigate to Keyboard Shortcuts and choose to Set Custom Shortcut. Name and Shortcut fields are up to your preference, to the Command line add:

element-desktop --proxy-server=socks5://127.0.0.1:1080

Make sure your Shortcut isn’t already taken by something else in the menu.

An example can look like this.

Alternatively you can add a keybinding via the CLI, using whatever config files you edit for your given desktop environment / window manager.

Create an alias

If you prefer to simply shorten the length of the command (or all your keybindings are already taken) then you can simply create an alias for this long-winded command (this example aliases that command to the single word element, but you can replace it with whatever you like):

Linux

alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080"

To make this alias persist, then add this to your .bashrc or .zshrc file (usually located in your $HOME directory) and source that file. This can be done by appending the alias command directly to the shell config file with one command.

For bash enter:

alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080" >> ~/.bashrc

For zsh enter:

alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080" >> ~/.zshrc

You can add the alias manually by opening your $HOME directory, enable hidden files (press ctrl + h) and open .bashrc or .zshrc file (based on your terminal setup) in a text editor, paste the string alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080" to the the end, save and exit. Start a new terminal and run element.

Mac

alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080"

To make this alias persist, then add this to your .zshrc (or .bashrc/.profile) file (usually located in your $HOME directory) and source that file. This can be done by appending the alias command directly to the shell config file with one command.

For zsh enter:

alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080" >> ~/.zshrc

For .bashrc or .profile just change the end of the command.

You can add the alias manually by opening your $HOME directory, enable hidden files (in Finder press Shift + Command + .) and open .zshrc file (or .bashrc/.profile) in a text editor, paste the string alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080" to the the end, save and exit. Start a new terminal and run element.

Now you can run Element through the Nym Mixnet with a single-word command.

Last change: 2023-09-19, commit: 8aa8f07c

Telegram NymConnect Integration

This is a shortened version of a Nym Community post written by Saliveja.

The purpose of the following manual is not to promote Telegram but so people can use it with the Nym mixnet if they wish to, should a situation ask for that. This privacy-enhances Telegram at the network level and allows users to access the application from locations like where the application was banned.

See also: Element (Matrix) over the Nym mixnet: private, decentralised and secure messaging.

Setup & Run

Here’s how to configure Telegram with NymConnect:

  1. Download and install NymConnect(https://nymtech.net/download-nymconnect/). For more releases, check out Github. NymConnect is available for Linux, Windows, and MacOS. On Linux make sure NymConnect is executable. Opening a terminal in the same directory and run:
    chmod +x ./<YOUR-NYM-CONNECT-VERSION>.AppImage
    
  2. Start NymConnect Telegram is added to NymConnect by default.
  3. Click connect - the host and port will now be displayed.
  4. Click on host or port to copy the value to the clipboard.
  5. Open the Telegram proxy settings. Linux: Telegram -> Settings -> Advanced -> Connection type -> Use custom proxy MacOS: Telegram -> Settings -> Advanced -> Data & Storage -> Connection Type -> Use custom Proxy Windows: Telegram -> Settings -> Data and Storage -> Use proxy
  6. Add a proxy with the Add proxy button.
  7. Select SOCKS5 and make sure the port details are the same as those generated by NymConnect. Alternatively, follow this link: https://t.me/socks?server=127.0.0.1&port=1080
  8. Save the proxy settings in Telegram.
  9. Telegram is now running through the Nym Mixnet and is privacy-enhanced! This allows you to connect from regions which blocked Telegram.
  10. Note if you remain idle on Telegram for a while you might lose connectivity and your messages might not get through via SOCKS5 proxy. If that happens reconnect your NymConnect and reset the proxy again.

Follow this video to see the steps on Telegram setup.

Now your Telegram runs over NymConnect.

NymConnect is currently available for several applications and service providers. Support for more apps is on the way. For any bug reports or feedback please reach out to us on Telegram or our Discord server.

Last change: 2023-09-19, commit: 8aa8f07c

Integration Options

If you’ve already gone through the different Quick Start options, you have seen the possibilities avaliable to you for quickly connecting existing application code to another Nym process.

This section assumes you wish to integrate with Nym into your application code.

The integrations FAQ has a list of common questions regarding integrating with Nym and Nyx, as well as commonly required links. This is a good place to start to get an overall idea of the tools and software avaliable to you.

If you wish to integrate with Nym to use the mixnet for application traffic, start with the mixnet integration page.

If you wish to integrate with the Nyx blockchain to use NYM for payments, start with the payment integration page.

Last change: 2023-09-19, commit: 8aa8f07c

Integrating with Nym for network privacy

If you are wanting to integrate Nym by using the Mixnet as a transport layer for application traffic, you will have to run one of the three Nym clients in order to connect to the Mixnet.

Connecting applications to the mixnet

SDK support

If your app is written in Typescript or Rust, then you can use the Typescript or Rust SDKs. These SDKs abstract away much of the messaging logic from your app, and allow you to run a Nym client as part of your application process, instead of having to run them seperately.

Choosing a client

In order to connect your application to the mixnet, you need to select one of three clients to use. These clients do the majority of the heavy-lifting with regards to cryptographic operations and routing under the hood, and all do basically the same thing: create a connection to a gateway, encrypt and decrypt packets sent to and received from the mixnet, and send cover traffic to hide the flow of actual app traffic from observers.

As outlined in the clients overview documentation there are three clients availiable to developers to use when connecting applications to the mixnet:

Websocket client

Your first option is the native websocket 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.

You can see an example of how to connect to and manage interactions with this client in the Simple Service Provider tutorial.

Webassembly client

If you’re working in JavaScript or Typescript in the browser, or building an edge computing app, you’ll likely want to choose the webassembly client.

It’s packaged and available on the npm registry, so you can npm install it into your JavaScript or TypeScript application.

The webassembly client is most easily used via the typescript sdk.

You can find example code in the examples section of the codebase, and in the typescript sdk docs.

SOCKS client

This 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.

You can find examples of how to utilise this client in the Quickstart section, and the SOCKS5 documentation.

In order to ensure uptime and reliability, it is recommended that you run some pieces of mixnet infrastructure. What infrastructure is necessary to run depends on the architecture of your application, and the endpoints that it needs to hit!

  • If you’re running a purely P2P application, then just integrating clients and having some method of sharing addresses should be enough to route your traffic through the mixnet.
  • If you’re wanting to place the mixnet between your users’ application instances and a server-based backend, you can use the network requester service provider binary to proxy these requests to your application backend, with the mixnet ‘between’ the user and your service, in order to prevent metadata leakage being broadcast to the internet.
  • If you’re wanting to route RPC requests through the mixnet to a blockchain, you will need to look into setting up some sort of service that does the transaction broadcasting for you. You can find examples of such projects on the community applications page.

Example application traffic flow

Initialization

First, we need to initalise an app and connect it to Nym.

       +-----------+
       |  Gateway  |
       +-----------+
             ^
             |
             |
             |
             |
             |
             |
   +-------------------+
   | +---------------+ |
   | |  Nym client   | |
   | +---------------+ |
   |         ^         |
   |         |         |
   |         |         |
   |         |         |
   |         v         |
   | +---------------+ |
   | | Your app code | |
   | +---------------+ |
   +-------------------+
    Your Local Machine

At the bottom we have an app. It consists of two parts:

  • your application specific logic
  • your Nym client - either running as a standalone process, or as part of the process of your app code if you’re using an SDK

Nym apps have a stable, potentially long-lasting relation to a gateway node. A client will register itself with a gateway, and get back an authentication token that it can then use to retrieve messages from the gateway later on.

Gateways serve a few different functions:

  • they act as an end-to-end encrypted message store in case your app goes offline.
  • they send encrypted surb-acks for potentially offline recipients, to ensure reliable message delivery
  • they offer a stable addressing location for apps, although the IP may change frequently

Sending messages to ourselves

The Nym client part of the app accepts messages from your code and automatically turns it into layer-encrypted Sphinx packets. If your message is too big to fit inside on Sphinx packet, it’ll be split into multiple packets with a sequence numbers to ensure reliable automatic reassembly of the full message when it gets to the recipient.

The app has now connected to the Gateway, but we haven’t sent a message to ourselves yet. Let’s do that now.


       +----------+              +----------+             +----------+
       | Mix Node |<-----------> | Mix Node |<----------->| Mix Node |
       | Layer 1  |              | Layer 2  |             | Layer 3  |
       +----------+              +----------+             +----------+
            ^                                                   ^
            |                                                   |
            |<--------------------------------------------------+
            |
            v
    +--------------+
    | Your gateway |
    +--------------+
            ^
            |
            |
            v
  +-------------------+
  | +---------------+ |
  | |  Nym client   | |
  | +---------------+ |
  |         ^         |
  |         |         |
  |         |         |
  |         v         |
  | +---------------+ |
  | | Your app code | |
  | +---------------+ |
  +-------------------+
   Your Local Machine**


** note that depending on the technical setup, the Nym client running on this machine may
be either a seperate process or embedded in the same process as the app code via one of our SDKs.

Let’s say your code code pokes a message hello world into the Nym client. The Nym client automatically wraps that message up into a layer encrypted Sphinx packet, adds some routing information and encryption, and sends it to its own gateway. The gateway strips the first layer of encryption, ending up with the address of the first mixnode it should forward to, and a Sphinx packet.

The gateway forwards the Sphinx packet containing the hello world message. Each mixnode in turn forwards to the next mixnode. The last mixnode forwards to the recipient gateway (in this case, our own gateway since we are sending to ourselves).

Our app has presumably not gone offline in the short time since the message was sent. So when the gateway receives the packet, it decrypts the packet and sends the (encrypted) content back to our app.

The Nym client inside the app decrypts the message, and your code receives the message hello world, again as a websocket event.

Messages are end-to-end encrypted. Although the gateway knows our app’s IP when it connects, it’s unable to read any of the message contents.

Sending messages to other apps

The process for sending messages to other apps is exactly the same, you simply specify a different recipient address. Address discovery happens outside the Nym system: in the case of a Service Provider app, the service provider has presumably advertised its own address. If you’re sending to a friend of yours, you’ll need to get a hold of their address out of band, maybe through a private messaging app such as Signal.


       +----------+              +----------+             +----------+
       | Mix Node |<-----------> | Mix Node |<----------->| Mix Node |
       | Layer 1  |              | Layer 2  |             | Layer 3  |
       +----------+              +----------+             +----------+
            ^                                                   ^
            |                                                   |
            |                                                   |
            v                                                   v
    +--------------+                                   +-----------------+
    | Your gateway |                                   | Service gateway |
    +--------------+                                   +-----------------+
            ^                                                    ^
            |                                                    |
            |                                                    |
            v                                                    v
  +-------------------+                                +-------------------+
  | +---------------+ |                                | +---------------+ |
  | |  Nym client   | |                                | |  Nym Client   | |
  | +---------------+ |                                | +---------------+ |
  |         ^         |                                |         ^         |
  |         |         |                                |         |         |
  |         |         |                                |         |         |
  |         v         |                                |         v         |
  | +---------------+ |                                | +---------------+ |
  | | Your app code | |                                | | Service Code  | |
  | +---------------+ |                                | +---------------+ |
  +-------------------+                                +-------------------+
   Your Local Machine**                               Service Provider Machine**


** note that depending on the technical setup, the Nym client running on these machines may
be either a seperate process or embedded in the same process as the app code via one of our SDKs.
Last change: 2023-09-19, commit: 8aa8f07c

Integrating with Nyx for payments

If you want to integrate with Nym in order to send NYM tokens (for instance, if running a NYM <-> BTC swap application, or using NYM for payments), then you will need to interact with the Nyx blockchain.

Nyx is the blockchain supporting the Nym network, hosting both the NYM and NYX cryptocurrencies, the CosmWasm smart contracts keeping track of the network, and (coming soon) facilitating zk-Nym credential generation. It is built with the Cosmos SDK.

Interacting with the Nyx blockchain

Check out the integration options in the Integration FAQ.

Chain information and RPC endpoints

You can find most information required for integration in the Cosmos Chain Registry and Keplr Chain Registry repositories.

We recommend that users wanting to integrate with Nyx for cryptocurrency payments set up their own RPC Node, in order to be able to reliably query the blockchain and send transactions without having to worry about relying on 3rd party validators.

The guide to setting up an RPC node can be found here.

Last change: 2023-09-19, commit: 8aa8f07c

Rust SDK

The Rust SDK allows developers building applications in Rust to import and interact with Nym clients as they would any other dependency, instead of running the client as a seperate process on their machine.

Read the docs.

Last change: 2023-09-19, commit: 8aa8f07c

Interacting with a Cosmos SDK Blockchain via the Mixnet with the Rust SDK

This tutorial is for Rust developers wanting to interact with the Rust SDK and take a first step at building a service with which to interact with a Cosmos SDK blockchain.

The key here is to think of the service as a proxy: it interacts with the blockchain on the client’s behalf, shielding the client from the Validator it interacts with, whilst also being shielded from the client by the mixnet.

This service also nicely highlights the limitations of the mixnet - even though with this code your metadata is shielded from the Validator, and even the service does not know your Nym address, application-level information such as a blockchain address is not made private, in virtue of the fact that using the mixnet provides solely network-level privacy. For information on what application-level privacy Nym offers, check out the coconut credential SDK example.

Last change: 2023-09-19, commit: 8aa8f07c

Tutorial Overview

This tutorial involves writing two pieces of code in Rust:

  • A client side binary used to construct a blockchain query and send this query to a service, which will query the Cosmos SDK blockchain and then pass the response back to the client (bear in mind this principle works for all blockchains - we’re just utilising the cosmrs library to interact with the Sandbox testnet blockchain in this tutorial). This query will be to query the balance of an account, in preparation for spending these tokens on a bandwidth credential in a subsequent tutorial.
  • A service which will listen out for requests from the mixnet, act on those requests, and anonymously reply to the client sending the requests.

You will learn how to do the following with the Rust SDK:

  • Create clients with manual storage settings.
  • Parse incoming traffic from the mixnet and reply anonymously using SURBs.

Services usually run on remote servers to assure reliable uptime and to unlink sender and receiver metadata. For demonstration purposes however, you will run both components on your local machine, looping messages through the mixnet to yourself.

You can find the code for these components here. You can use it as a reference while building or simply download it and follow along as you progress through the tutorial.

Notice that this tutorial attempts to use very few external libraries. This tutorial is not showing you how to build production-grade code, but to understand how to connect and send messages to, as well as receive messages from, the mixnet.

Sidenote: What is a Service / Service Provider?

‘Service’ or ‘Service Provider’ are catchall names used to refer to any type of app that can communicate with the mixnet via a Nym client - in this case, one embedded in its app process via the Rust SDK.

The first SP to have been released is the Network Requester - a binary that receives a network request from the mixnet, performs that request (e.g. authenticating with a message server and receiving new messages for a user) and then passes the response back to the user who requested it anonymously, shielding their metadata from the message server.

The SP you will build in this tutorial is far more simple than this, showing you how to approach building something that can:

  • connect to the mixnet,
  • listen for messages, and
  • perform some action with them - in this case, query a Cosmos SDK blockchain.

However, once you see how easy it is to integrate with the mixnet for traffic transport, you will be able to build apps with real-world uses easily.

Last change: 2023-09-19, commit: 8aa8f07c

Preparing Your Environment

Prerequisites

  • Rust & cargo

Creating your Project Structure

  • Make a new cargo project:
cargo new nym-cosmos-service
  • Create the following directory structure and files:
.
├── Cargo.toml
├── bin
│   ├── client.rs
│   └── service.rs
└── src
    ├── client.rs
    ├── lib.rs
    └── service.rs

3 directories, 6 files
  • Add the following dependencies to your Cargo.toml file:
[dependencies]
clap = { version = "4.0", features = ["derive"] }
cosmrs = "=0.14.0"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
serde = "1.0.152"
serde_json = "1.0.91"
anyhow = "1.0.72"

These are non Nym-specific dependencies for the project. clap is for setting up the CLI commands, cosmrs for cosmos-specific types and functionality, tokio for the async/await environment, and serde for (de)serialisation. anyhow is for catch-all error handling.

  • Next add Nym-specific dependencies. Since these libraries are not yet on crates io then you need to import them from the Nym monorepo:
nym-sdk = { git = "https://github.com/nymtech/nym", rev = "85a7ec9f02ca8262d47eebb6c3b19d832341b55d" }
nym-sphinx-addressing = { git = "https://github.com/nymtech/nym", rev = "85a7ec9f02ca8262d47eebb6c3b19d832341b55d" }
nym-validator-client = { git = "https://github.com/nymtech/nym", rev = "85a7ec9f02ca8262d47eebb6c3b19d832341b55d" }
nym-bin-common = { git = "https://github.com/nymtech/nym", rev = "85a7ec9f02ca8262d47eebb6c3b19d832341b55d" }
nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", rev = "85a7ec9f02ca8262d47eebb6c3b19d832341b55d" }

The sphinx dependencies are for packet- and address-related functionality, the validator-client for Nyx blockchain specific configs, common for client logging, and the sdk for SDK functionality: creating and managing client storage and connections, and sending and receiving messages to and from the mixnet.

  • Finally add the following underneath your [dependencies]:
[[bin]]
name = "client"
path = "bin/client.rs"

[[bin]]
name = "service"
path = "bin/service.rs"

This defines multiple binaries to run in a single cargo project, as outlined here.

Last change: 2023-09-19, commit: 8aa8f07c

Preparing Your Lib

Now move on to preparing shared data structures and functions in src/lib.rs.

These include the request and response types the client and the service will be passing through the mixnet, as well as shared functions such as client creation, and message parsing.

Dependencies

The dependencies for the shared lib file are the following:

#![allow(unused)]
fn main() {
use anyhow::bail;
use cosmrs::AccountId;
use nym_sdk::mixnet::{
    AnonymousSenderTag, MixnetClient, MixnetClientBuilder, ReconstructedMessage, StoragePaths,
};
use nym_validator_client::nyxd::Coin;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

pub mod client;
pub mod service;
}

Since this is the file where client creation and message parsing are handled, the various nym_sdk imports, as well as serde’s (de)serialisation functionality, are required. PathBuf is for reading filepaths, cosmrs types are required for defining Nyx blockchain accounts, and the Coin type from the nyxd_validator_client is for our Coin balance request and response. anyhow is for easy error handing.

Constants

Below this are the chain-related const variables. These have been hardcoded for this demo.

#![allow(unused)]
fn main() {
pub const DEFAULT_VALIDATOR_RPC: &str = "https://sandbox-validator1.nymtech.net";
pub const DEFAULT_DENOM: &str = "unym";
pub const DEFAULT_PREFIX: &str = "n";
}

These define the RPC endpoint your service will use to interact with the blockchain - in this case the Sandbox testnet - as well as the expected coin denomination, and Bech32-prefix of addresses.

Shared Data Structures

Define the following structs for our different request and responses that will be serialised and sent through the mixnet between your client and service binaries:

#![allow(unused)]
fn main() {
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct BalanceRequest {
    pub validator: String,
    pub account: AccountId,
}

#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct BalanceResponse {
    pub balance: Coin,
}

#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub enum RequestTypes {
    Balance(BalanceRequest),
}

impl RequestTypes {
    pub fn serialize(&self) -> Vec<u8> {
        serde_json::to_vec(self).expect("serde failure")
    }

    pub fn try_deserialize<M: AsRef<[u8]>>(raw: M) -> anyhow::Result<Self> {
        serde_json::from_slice(raw.as_ref()).map_err(Into::into)
    }
}

#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub enum ResponseTypes {
    Balance(BalanceResponse),
}

impl ResponseTypes {
    pub fn serialize(&self) -> Vec<u8> {
        serde_json::to_vec(self).expect("serde failure")
    }

    pub fn try_deserialize<M: AsRef<[u8]>>(raw: M) -> anyhow::Result<Self> {
        serde_json::from_slice(raw.as_ref()).map_err(Into::into)
    }
}
}

The above data types are pretty straightforward. Even though there are only one instance of a request type (sent from client -> mixnet -> service) and one of a response type (service -> mixnet -> client) so far, a pair of enums has been defined to contain additional response and request types that will be added in part 2 of this tutorial, when adding credential functionality.

BalanceRequest will be used when requesting the service to query the token balance of the supplied address on the client’s behalf. You can see the information that will be returned from the chain to the service, and from the service to the client, in BalanceResponse.

Custom serialistion and deserialisation have been implemented for each enum for ease of future modification and testing.

Shared Functions

Now to define functions shared by the client and service binaries.

Client Creation

The following function is called on startup by each binary, with the config_path being a filepath for storing client config:

#![allow(unused)]
fn main() {
// create our client with specified path for key storage
pub async fn create_client(config_path: PathBuf) -> MixnetClient {
    let config_dir = config_path;
    let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap();
    let client = MixnetClientBuilder::new_with_default_storage(storage_paths)
        .await
        .unwrap()
        .build()
        .await
        .unwrap();

    client.connect_to_mixnet().await.unwrap()
}
}

If no config files exist at the location designated by config_path (in this case /tmp/service) then the following files are generated:

service
├── ack_key.pem
├── db.sqlite
├── db.sqlite-shm
├── db.sqlite-wal
├── gateway_details.json
├── gateway_shared.pem
├── persistent_reply_store.sqlite
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem

1 directory, 11 files

If keys and config already exist at this location, re-running this function will not overwrite them.

Listening for & Parsing Incoming messages

Next to define two functions: one for listening for messages from the mixnet (used by service), and one for handling a response to a request (used by client).

Both functions attempt to deserialise the vec of ReconstructedMessages that are reconstructed by the client from delivered Sphinx packets after decryption.

handle_request performs one additional function - parsing the sender_tag from the incoming reconstructed message. This is the randomised alphanumeric string used to identify a bucket of SURBs (Single Use Reply Blocks) that are sent along with any outgoing message by default. More information about them can be found here but all that is necessary to know for now is that these are pre-addressed packets that clients send out with their messages. Any reply to their message that is to be sent back to them back be written to the payload of these packets, but without the replying party being able to see the destination that the reply is being sent to. This allows for services to anonymously reply to clients without being able to doxx them by knowing their Nym address.

#![allow(unused)]
fn main() {
pub fn handle_response(message: ReconstructedMessage) -> anyhow::Result<ResponseTypes> {
    ResponseTypes::try_deserialize(message.message)
}

pub fn handle_request(
    message: ReconstructedMessage,
) -> anyhow::Result<(RequestTypes, Option<AnonymousSenderTag>)> {
    let request = RequestTypes::try_deserialize(message.message)?;
    Ok((request, message.sender_tag))
}
}

Before moving on to the client and service code, one more function is needed. This allows for both binaries to parse empty incoming messages that they might receive. This is necessary as incoming SURBs, as well as requests for more SURBs, contain empty data fields.

#![allow(unused)]
fn main() {
pub async fn wait_for_non_empty_message(
    client: &mut MixnetClient,
) -> anyhow::Result<ReconstructedMessage> {
    while let Some(mut new_message) = client.wait_for_messages().await {
        if !new_message.is_empty() {
            return Ok(new_message.pop().unwrap());
        }
    }

    bail!("did not receive any non-empty message")
}
}
Last change: 2023-09-19, commit: 8aa8f07c

Preparing Your Client

Start by creating the startup logic of your client in bin/client.rs - creating a Nym client and connecting to the mixnet (or just connecting if your client has been started before and config already exists for it), and defining and running commands.

Dependencies

Import the following dependencies:

#![allow(unused)]
fn main() {
use clap::{Args, Parser, Subcommand};
use chain_query::{client::query_balance, create_client};
use nym_sdk::mixnet::Recipient;
use nym_validator_client::nyxd::AccountId;
use nym_bin_common::logging::setup_logging;
}

clap is used so different commands can be passed to the client (even though we’re only defining one function in this first part of the tutorial, more will be added in subsequent chapters). nym_sdk::mixnet::Recipient is the type used to define the recipient of a mixnet message, nym_bin_common::logging::setup_logging is the logging setup for client’s Nym client, and chain_query imports the create_client and query_balance functions created on the previous page.

CLI Command with Clap

The following simply defines the commands that the client can perform. For the moment, there is only one: the query_balance function created in the previous section.

As with the data structures, this structure is being used for ease of adding future commands in subsequent tutorials.

#![allow(unused)]
fn main() {
#[derive(Debug, Parser)]
#[clap(name = "rust sdk demo - chain query service")]
#[clap(about = "query the sandbox testnet blockchain via the mixnet... part 2 coming soon")]
struct Cli {
    #[clap(subcommand)]
    command: Option<Commands>,
}

#[derive(Debug, Subcommand)]
enum Commands {
    QueryBalance(QueryBalance),
}

#[derive(Debug, Args)]
struct QueryBalance {
    /// the account we want to query
    account: AccountId,
    /// the address of the broadcaster service - this submits txs and queries the chain on our behalf
    sp_address: String,
}
}

main()

This is the root logic of the client. Using [tokio](https://tokio.rs/) for the async runtime, this function performs the following functions:

  • If not already existing, create a Nym client with config at /tmp/client. Otherwise load the already existing client from this config.
  • Matche the command from the CLI - in this instance, the QueryBalance function which will be defined in the next section. This creates a BalanceRequest and sends this to the service, before returning the response back to the main thread and print this to the console.
  • Perform a proper shutdown of the Nym client.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    setup_logging();
    let cli = Cli::parse();
    let mut client = create_client("/tmp/client2".into()).await;
    let our_address = client.nym_address();
    println!("\nclient's nym address: {our_address}");

    match cli.command {
        Some(Commands::QueryBalance(QueryBalance {
            account,
            sp_address,
        })) => {
            println!("\nsending bank balance request to service via mixnet");
            let sp_address = Recipient::try_from_base58_string(sp_address).unwrap();
            let returned_balance = query_balance(account, &mut client, sp_address).await?;
            println!("\nreturned balance is: {}", returned_balance);
        }
        None => {
            println!("\nno command specified - nothing to do")
        }
    }
    println!("\ndisconnecting client");
    client.disconnect().await;
    println!("client disconnected");
    Ok(())
}
Last change: 2023-09-19, commit: 8aa8f07c

Preparing Your Client pt2

Open src/client.rs. This is where the logic of the command from the match statement in bin/client.rs is defined.

Dependencies

#![allow(unused)]
fn main() {
use crate::{handle_response, wait_for_non_empty_message, RequestTypes, DEFAULT_VALIDATOR_RPC};
use cosmrs::AccountId;
use nym_sdk::mixnet::MixnetClient;
use nym_sphinx_addressing::clients::Recipient;
use nym_validator_client::nyxd::Coin;
}

As well as importing message-handling functionality, request types, and the default RPC endpoint, this file relies on the AccountId type to construct blockchain addresses, the MixnetClient for interacting with the mixnet, the Recipient type to construct mixnet recipient addresses, and the Coin type for properly handling the returned balance of the account that will be queried.

Querying via the Mixnet

The following is used to construct a BalanceRequest, send this to the supplied service address, and then handle the response, matching it to a ResponseType (in this case the only expected response, a BalanceResponse).

The actual sending of the request is performed by client.send_message: sending the serialised BalanceRequest to the supplied Nym address (the Recipient imported from the nym_sphinx_addressing crate). It is sending the default number of SURBs along with the message as the third argument, defined here.

#![allow(unused)]
fn main() {
pub async fn query_balance(
    account: AccountId,
    client: &mut MixnetClient,
    sp_address: Recipient,
) -> anyhow::Result<Coin> {
    // construct balance request
    let message = RequestTypes::Balance(crate::BalanceRequest {
        validator: DEFAULT_VALIDATOR_RPC.to_owned(), // rpc endpoint for broadcaster to use
        account,
    });

    // send serialised request to service via mixnet
    let _ = client
        .send_message(sp_address, message.serialize(), Default::default())
        .await;

    let received = wait_for_non_empty_message(client).await?;

    // listen for response from service
    let sp_response = handle_response(received)?;

    // match JSON -> ResponseType
    let res = match sp_response {
        crate::ResponseTypes::Balance(response) => {
            println!("{:#?}", response);
            response.balance
        }
    };

    Ok(res)
}
}

That is all the client code written: now to move on to the service that will be interacting with the blockchain on behalf of the client.

Last change: 2023-09-19, commit: 8aa8f07c

Preparing Your Service

In bin/src.rs define the startup and response logic of the service. Client connection / config reading happens as it does in bin/client.rs.

Dependencies

#![allow(unused)]
fn main() {
use chain_query::{
    create_client, handle_request,
    service::{create_broadcaster, get_balance},
    BalanceResponse, RequestTypes, ResponseTypes,
};
use nym_sphinx_anonymous_replies::{self, requests::AnonymousSenderTag};
use nym_bin_common::logging::setup_logging;
use nym_sdk::mixnet::MixnetMessageSender;
}

The imports from chain_query are most of the data types and functions defined in the previous sections of this tutorial.

The AnonymousSenderTag type is used for SURBs.

main()

Also using tokio for the async runtime, main does the following:

  • Create a Nym client with config at /tmp/service, or load the existing client from this config directory.
  • Create a broadcaster - this is used by the service to interact with the blockchain, using the consts defined in src/lib.rs as chain config.
  • Listen out for incoming messages, and in much the same way as the client, handle and match the incoming request.
  • Using the sender_tag, anonymously reply to the client with the response from the blockchain without having to know the client’s Nym address.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    setup_logging();
    let mut client = create_client("/tmp/service".into()).await;
    let our_address = client.nym_address();
    println!("\nservice's nym address: {our_address}");
    // the httpclient we will use to broadcast our query to the blockchain
    let broadcaster = create_broadcaster().await?;
    println!("listening for messages, press CTRL-C to exit");

    while let Some(received) = client.wait_for_messages().await {
        for msg in received {
            let request = match handle_request(msg) {
                Ok(request) => request,
                Err(err) => {
                    eprintln!("failed to handle received request: {err}");
                    continue;
                }
            };

            let return_recipient: AnonymousSenderTag = request.1.expect("no sender tag received");
            match request.0 {
                RequestTypes::Balance(request) => {
                    println!("\nincoming balance request for: {}\n", request.account);

                    let balance: BalanceResponse =
                        get_balance(broadcaster.clone(), request.account).await?;

                    let response = ResponseTypes::Balance(balance);

                    println!("response from chain: {:#?}", response);

                    println!("\nreturn recipient surb bucket: {}", &return_recipient);
                    println!("\nsending response to {}", &return_recipient);
                    // send response back to anon requesting client via mixnet
                    let _ = client
                        .send_reply(return_recipient, &serde_json::to_string(&response)?)
                        .await;
                }
            }
        }
    }

    Ok(())
Last change: 2023-09-19, commit: 8aa8f07c

Preparing Your Service pt2

Now to define the logic of creating the broadcaster for interacting with the blockchain, and querying it in src/service.rs.

Dependencies

The following dependencies are for creating a client to interact with the blockchain, and deal with the returned Coin type in BalanceResponse.

#![allow(unused)]
fn main() {
use crate::{BalanceResponse, DEFAULT_DENOM, DEFAULT_VALIDATOR_RPC};
use cosmrs::rpc::HttpClient;
use cosmrs::AccountId;
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
}

Creating a broadcaster

The broadcaster is an HttpClient taken from cosmrs, created with the DEFAULT_VALIDATOR_RPC as its default endpoint. The service will use this to query the Sandbox testnet chain.

#![allow(unused)]
fn main() {
pub async fn create_broadcaster() -> anyhow::Result<HttpClient> {
    let broadcaster: HttpClient = HttpClient::new(DEFAULT_VALIDATOR_RPC)?;
    Ok(broadcaster)
}
}

Querying the Chain

Now to write the logic for querying the chain, using the broadcaster created in the previous step to query for the balance of the account that the client sent via the mixnet in the BalanceRequest. This function returns a BalanceResponse containing a Coin type, denoting the balance and denom returned by get_balance().

#![allow(unused)]
fn main() {
pub async fn get_balance(
    broadcaster: HttpClient,
    account: AccountId,
) -> anyhow::Result<BalanceResponse> {
    let balance = broadcaster
        .get_balance(&account, DEFAULT_DENOM.to_string())
        .await
        .unwrap()
        .unwrap();
    Ok(BalanceResponse {
        balance: Coin {
            amount: balance.amount,
            denom: balance.denom,
        }, 
    })
}
}
Last change: 2023-09-19, commit: 8aa8f07c

Querying the Chain

Now that all the code has been written, time to query the blockchain.

To test against the account operating one of the mix nodes on the testnet, use n1lcutqz94k739s39u26rvexql40ehf42zd27fwe in the following instructions:

# build the binaries
cargo build --release

# open two console windows. In one run the following
./target/release/service

# copy the printed Nym address from the console - this is used by the client when running the chain query

# in the other run
./target/release/client query-balance n1lcutqz94k739s39u26rvexql40ehf42zd27fwe <SERVICE_ADDRESS_FROM_CLIPBOARD>

The following happens:

  • client and service both start their Nym clients and log the address. service is listening for incoming messages from the mixnet.
  • client sends a request to service using the supplied n1... address as the Nyx account to query the balance of, and the supplied Nym address to communicate with this instance of service.
  • service queries the Sandbox testnet blockchain using the broadcaster http client. It then serialises the response and returns it using SURBs to the client.

All in all, quite simple. By using the service as a proxy, the client never interacts with the blockchain, thus is not revealing metadata to the operator of the Validator nor any entities monitoring its incoming and outgoing traffic. Furthermore, the client doesn’t even need to share its Nym address with the service, as the service is able to reply via SURBs.

Creating Sandbox Account

If you wish to create an account on Sandbox to use instead of the supplied account above, the easiest way is by building the nym-cli tool and using that to create one:

# start from the root of the nym monorepo
cd tools/nym-cli
# build
cargo build --release
# create account using Sandbox testnet environment
../../target/release/nym-cli --config-env-file ../../envs/sandbox.env account create

However, since this account is fresh, it won’t have any tokens. Querying the balance will still work obviously, it will just return 0.

Get Tokens

We’re working on getting the faucet up and running again in preparation for part 2 of this tutorial: using the tokens you have privately checked the balance of to generate a bandwidth credential.

If you wish to get testnet tokens already then feel free to ask in the Dev channel on Matrix.

Last change: 2023-09-19, commit: 8aa8f07c

Typescript

Tutorial code in this section is built to interact with a standalone Nym client. You can read about interacting with standalone clients here, although it is usually preferable to use the Typescript SDK.

Last change: 2023-09-19, commit: 8aa8f07c

Building a Simple Service Provider

This tutorial is the best place to start for developers new to Nym. You will learn how to build a minimum viable privacy-enabled application (PEApp) able to send and receive traffic via the mixnet.

This tutorial is less about building an immediately useful application, and more about beginning to understand:

  • Sending messages through the mixnet to another Nym client
  • Receiving messages from the mixnet and handling them
  • Anonymous replies with Single Use Reply Blocks (SURBs)

That said, this tutorial will give you the skeleton of an application, onto which you can add more useful functionality.

Last change: 2023-09-19, commit: 8aa8f07c

Tutorial Overview

Components Created in this Tutorial

This tutorial involves writing two pieces of code in Typescript:

  • A User Client (UC) with which you can access the mixnet through a browser on your local machine. You will use this to communicate with the second component outlined below.
  • A Service Provider (SP) which can communicate with the UC via the mixnet.

Additionally you will learn how to configure a pair of Nym Websocket Clients which both components use to connect to and communicate with the Mixnet.

SPs usually run on remote servers to assure reliable uptime and to unlink sender and receiver metadata. For demonstration purposes however, you will run both components on your local machine, looping messages through the mixnet to yourself.

       +----------+              +----------+             +----------+ 
       | Mix Node |<-----------> | Mix Node |<----------->| Mix Node |
       | Layer 1  |              | Layer 2  |             | Layer 3  |
       +----------+              +----------+             +----------+  
            ^                                                   ^      
            |                                                   |      
            |<--------------------------------------------------+
            |                                                          
            v                                                        
    +--------------+                                
    | Your gateway |                               
    +--------------+                               
            ^                                       
            |                                                                      
            |                                                                         
            v                                                        
+-------------------------------------------+                         
|                                           |                        
|  +------------+     +------------+        |                      
|  | Nym Client |     | Nym Client |        |                     
|  +------------+     +------------+        |                    
|        ^                  ^               |                   
|        |                  |               |                  
|        |                  |               |                 
|        v                  v               |                
|  +-------------+    +------------------+  |               
|  | User Client |    | Service Provider |  |              
|  +-------------+    +------------------+  |             
|                                           |            
+-------------------------------------------+           
            Your Local Machine          

Aims of this Tutorial

  • Create a user-friendly experience for sending data through the mixnet via a simple form accessible through a web browser.
  • Configure and use a pair of Nym Websocket Clients.
  • Send a properly formatted message through the mixnet to the SP from a browser-based GUI.

You can find the code for these components here. You can use it as a reference while building or simply download it and follow along as you progress through the tutorial.

Notice that this tutorial attempts to use very few external libraries (the User Client codebase is basically vanilla Typescript!). This tutorial is not showing you how to build production-grade code, but to understand how to connect and send messages to, as well as recieve messages from, the mixnet.

Sidenote: What is a Service Provider?

‘Service Provider’ is a catchall name used to refer to any type of app that can communicate with the mixnet via a Nym client.

The first SP to have been released is the Network Requester - a binary that receives a network request from the mixnet, performs that request (e.g. authenticating with a message server and receiving new messages for a user) and then passes the response back to the user who requested it anonymously, shielding their metadata from the message server.

The SP you will build in this tutorial is far more simple than this. It is just to show you how to approach building something that can:

  • connect to the mixnet,
  • listen for messages, and
  • perform some action with them (in this case, log them in a console and reply to the original sender).

However, once you see how easy it is to integrate with the mixnet for traffic transport, you will be able to build apps with real-world uses easily.

Last change: 2023-09-19, commit: 8aa8f07c

Preparing Your User Client Environment

Prerequisites

  • NodeJS & npm
  • typescript

Preparing your TypeScript environment

  • Make a new directory called simple-service-provider-tutorial containing a directory named user-client:
mkdir -p simple-service-provider/user-client
  • Create a package.json and install dependencies:
cd simple-service-provider/user-client 
npm init  
npm install typescript # allows you to write and use typescript 
npm install ts-node --save-dev # allows you to build a typescript application in a NodeJS environment 
  • Create a tsconfig.json containing the following:
{
    "compilerOptions": {
        "module": "commonjs",
        "esModuleInterop": true,
        "target": "es6",
        "moduleResolution": "node",
        "sourceMap": true,
        "outDir": "dist"
    },
    "lib": ["es2015"]
}

Preparing your Bundler

  • We will use the Parcel bundler to build and run our app locally. Parcel also supports hot reloading, making for a nicer developer experience when working on our app. Install it with:
npm install parcel-bundler
  • Create the file structure for our frontend code:
mkdir src 
touch src/index.html src/index.ts

At this point your directory should look like this (check yourself with tree -L 2 simple-service-provider/):

simple-service-provider/
└── user-client
    ├── node_modules
    ├── package.json
    ├── package-lock.json
    ├── src
    └── tsconfig.json

4 directories, 3 files

And user-client/src/ should look like this:

user-client/src
├── index.html
└── index.ts

1 directory, 2 files
  • Time to check everything is working. Paste the following into src/index.html:
<!DOCTYPE html>
<html>
    <head>
        <title>App Test</title>
        <meta charset="utf-8"/>
    </head>
    <body>
        <h1>Test</h1>
        <div id="app"></div>
        <script src="index.ts"></script>
    </body>
</html>
  • Paste the following into src/index.ts
console.log('test log')
  • Add the following to package.json in the "scripts" array, above "test":
"start": "parcel src/index.html"
  • npm start should return:
> user-client@1.0.0 start
> parcel src/index.html

Server running at http://localhost:1234
✨  Built in 1.57s.

Open localhost:1234 in your browser. Your web application should be up and running, with Test displayed in the browser window. Checking the console.log output is done by right-clicking on the browser and selecting Inspect, then navigating to the Console section of the resulting panel. You should see the message test log displayed there.

Last change: 2023-09-19, commit: 8aa8f07c

Building Your User Client

Adding Core Functionality

Most of the work here will be configuring and adding functionality to src/index.ts file, allowing you to establish and handle the websocket connection to your local Nym client, and create and send messages to the SP.

  • Replace the existing content of src/index.ts with:
async function main() {}

function connectWebsocket(url) {
    return new Promise(function (resolve, reject) {
        var server = new WebSocket(url);
        console.log('connecting to Websocket Server (Nym Client)...')
        server.onopen = function () {
            resolve(server);
        };
        server.onerror = function (err) {
            reject(err);
        };
      
    });
}
    
main();

main() will the majority of the app’s logic. It’s best to declare it at the start of the file and call it at the end to run when launching the application.

connectWebsocket(url) returns a Promise that attempts to create a websocket connection to url. If the connection is successful, you will get a notification in your running application in the browser, as well as the connected Nym client. If it fails an error will be displayed in the browser.

  • Now to implement the functions that will handle DOM (Document Object Model) manipulation. Add the following below connectWebsocket():
function handleResponse(resp) {
    try {
        let response = JSON.parse(resp.data);
        if (response.type == "error") {
            displayJsonResponse("Server responded with error: " + response.message);
        } else if (response.type == "selfAddress") {
            ourAddress = response.address;
            displayClientMessage("Our address is:  " + ourAddress + ", we will now send messages to ourself.");
        } else if (response.type == "received") {
            handleReceivedTextMessage(response)
        }
    } catch (_) {
            displayJsonResponse(resp.data)
    }
}
       
function handleReceivedTextMessage(message) {
    const text = JSON.parse(message.message);
    displayJsonResponse(text);
}
        
// Display websocket responses in the Activity Log.
function displayJsonResponse(message) {
    let receivedDiv = document.createElement("div")
    let paragraph = document.createElement("p")
    paragraph.setAttribute('style', 'color: orange')
    let textNode = document.createTextNode("received >>> " + message.text)
    paragraph.appendChild(textNode)
    
    receivedDiv.appendChild(paragraph)
    document.getElementById("output").appendChild(receivedDiv)
}

function displayClientMessage(message) {
    document.getElementById("output").innerHTML += "<p>" + message + "</p >";
}

handleResponse() parses the type of any messages received from the websocket, and handles forwarding the message on to the appropriate function depending on this type. You can find documentation on these types here.

handleReceivedTextMessage() ensures that data is json data before displaying on the UI.

displayJsonResponse() is responsible for displaying received messages on the UI, creating a new <p> HTML element for each message that needs to be displayed on screen.

displayClientMessage() displays the address of the connected Nym client.

  • Declare the following variables above main()
var ourAddress:          string;
var targetAddress:       string;
var websocketConnection: any;

ourAddress takes the value of the connected Nym client address.

targetAddress will be the Nym address of the SP.

websocketConnection populated upon a successful response from connectWebsocket().

  • Add the following to main():
async function main() {
    var port = '1977' // Nym Websocket Client listens on 1977 by default.
    var localClientUrl = "ws://127.0.0.1:" + port;
    
    // Set up and handle websocket connection to our desktop client.
    websocketConnection = await connectWebsocket(localClientUrl).then(function (c) {
        return c;
    }).catch(function (err) {
        displayClientMessage("Websocket connection error. Is the client running with <pre>--connection-type WebSocket</pre> on port " + port + "?");
    })

    websocketConnection.onmessage = function (e) {
        handleResponse(e);
    };
    
    sendSelfAddressRequest();
    
    // Set up the send button
    const sendButton = document.querySelector('#send-button');
    
    sendButton?.addEventListener('click', function handleClick(event) {
        sendMessageToMixnet(); 
    });

And between main() and displayClientMessage():

function sendSelfAddressRequest() {
    var selfAddress = {
        type: "selfAddress"
    }
    displayJsonSend(selfAddress);
    websocketConnection.send(JSON.stringify(selfAddress));
}

sendSelfAddressRequest() sends a selfAddress message to the connected websocket client, passing the response to displayJsonSend() to be displayed on your UI.

main() now contains logic for: connecting to a local Nym client, getting its address with a selfAddress message, and displaying it on the UI. Now your app can display its connection status, letting you know whether it is(n’t) connected to a running client!

  • Underneath sendSelfAddressRequest() implement a function to send messages down the websocket connection to the SP:
function sendMessageToMixnet() {

    var nameInput = (<HTMLInputElement>document.getElementById("nameInput")).value;
    var textInput = (<HTMLInputElement>document.getElementById("textInput")).value;
   
    const messageContentToSend = {
        name : nameInput,
        comment : textInput,
    }
    
    const message = {
        type: "sendAnonymous",
        message: JSON.stringify(messageContentToSend),
        recipient: targetAddress,
        replySurbs: 5
    }
    
    displayJsonSend(message);
    websocketConnection.send(JSON.stringify(message));
}

Nym clients accept messages in either binary or JSON formats. Since you are sending JSON data, you need to stringify any messages you wish to send through the mixnet.

You are sending replySURBs along with the message to the SP. This allows the SP to reply to you without you having to doxx yourself and supply a ‘return address’ in a readable form to it. TLDR; SURBs allow for anonymous replies from mixnet services!

  • Below sendMessageToMixnet(), add the following:
function displayJsonSend(message) {
    let sendDiv = document.createElement("div")
    let paragraph = document.createElement("p")
    paragraph.setAttribute('style', 'color: #36d481')
    let paragraphContent = document.createTextNode("sent >>> " + JSON.stringify(message))
    paragraph.appendChild(paragraphContent)
            
    sendDiv.appendChild(paragraph)
    document.getElementById("output").appendChild(sendDiv)
}

displayJsonSend() displays sent messages in the “Activity Log” section of the UI.

  • Replace the contents of src/index.html with the following:
<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Mixnet Websocket Starter Client</title>
        <link rel="stylesheet" href="../assets/styles.css"/>
    </head>
    <body>
        <div class="content" role="main">
            <div class="toolbar">
                <h3>Mixnet Websocket Starter User Client</h3>
            </div>
            
            <div class="section-container">
               
                <label for="nameInput" class="form-field-label">Moniker</label>
                <input id="nameInput" type="text" value="An0n" name="nameInput">

                <label for="textInput" class="form-field-label">Comment</label>
                <input id="textInput" type="text" value="I would like to use your private service" name="textInput">
         
                <div id="send-button">
                    <label for="send-button" class="submit-button">Send</label>
                </div>
            </div>
        </div>
        
        <div class="" style="margin-left:20px;max-width: fit-content;">
            <div style="color: white;margin-bottom: 2rem;">
                <h4>How it works</h4>
                <p>Once you have started your Nym Websocket client, you can fill out the form and send data to the Service Provider via mixnet using the <b>"Send"</b> button.</p>
                <p>Below, you can see the activity log. <b style='color: #36d481;'>Sent</b> messages will display in <b style='color: #36d481;'>green</b> while <b style='color: orange;'>received</b> messages will display in <b style='color: orange;'>orange</b>.</p>
            </div>
        </div>
        
        <h3 style="margin-left:10px">Activity Log</h3>
        
        <p class="output-container">
            <span id="output"></div>
        </p>
        <script src="index.ts"></script>
    </body>
</html>

Lets add the finishing touches to the UI by adding in the stylesheet which we specified at the top of index.html:

mkdir -p assets
touch assets/styles.css

# grab the stylesheet from the remote repo and save it to the newly created css file
curl https://raw.githubusercontent.com/nymtech/developer-tutorials/main/simple-service-provider-tutorial/user-client/assets/styles.css -o assets/styles.css
  • Return back to your terminal and run:
npm start

Return to localhost:1234) and you should see an updated UI.

Connecting to your Nym Client

Follow instructions in the Nym websocket client documentationto init and run a client then refresh your browser window. You should see a successful response, including a Nym address, in the ‘Activity Log’ of the UI

Your User Client application code is connected to a Nym websocket client, and ready to send messages through the mixnet!

In the next section, you will build the Service application you will send these messages to.

Last change: 2023-09-19, commit: 8aa8f07c

Preparing Your Service Provider Environment

  • Now to move on to prepare our development environment for the Service Provider code. Create a directory for it:
# run this from the root of `simple-service-provider/`
mkdir service-provider
cd service-provider
  • Create a package.json:
npm init
  • Inside your newly generated package.json, paste in the following code:
{
    "name": "service-provider",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "start:dev": "nodemon",
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "devDependencies": {
        "@types/node": "^18.14.0",
        "@types/ws": "^8.5.4",
        "nodemon": "^2.0.20",
        "ts-node": "^10.9.1",
        "typescript": "^4.8.4"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "ws": "^8.12.0"
    }
}
  • install dependecies:
npm install
  • create a tsconfig.json file containing the following:
{
    "compilerOptions": {
    "target": "es2017", 
    "lib": [
        "es6"
    ],
    "module": "Node16", 
    "rootDir": "src", 
    "resolveJsonModule": true, 
    "allowJs": true,                      
    "outDir": "build", 
    "esModuleInterop": true, 
    "forceConsistentCasingInFileNames": true, 
    "strict": true, 
    "noImplicitAny": true, 
    "skipLibCheck": true 
    }
}
  • You will use Nodemon to reload your app on code changes. Create a nodemon.json file in the same directory which will act as our nodemon configuration. Paste in the following code inside that file:
{
    "watch": [
        "src"
    ],
    "ext": ".ts,.js",
    "ignore": [],
    "exec": "ts-node ./src/index.ts"
}
  • Finally, create a typescript file for our app logic:
mkdir src
touch src/index.ts

At this point your directory should look like this (check yourself with tree -L 2 simple-service-provider):

simple-service-provider
├── service-provider
│   ├── node_modules
│   ├── nodemon.json
│   ├── package.json
│   ├── package-lock.json
│   ├── src
│   └── tsconfig.json
└── user-client
    ├── node_modules
    ├── package.json
    ├── package-lock.json
    ├── src
    └── tsconfig.json

7 directories, 7 files

And service-provider/src/ should look like this:

service-provider/src
└── index.ts

1 directory, 1 file
Last change: 2023-09-19, commit: 8aa8f07c

Building Your Service Provider

Since a lot of logic you will write in this section is similar or the same to UC code you just written, this section will only focus on SP-specific functionality

  • Paste the following code into src/index.ts
import WebSocket, { MessageEvent } from "ws";

var ourAddress:          string;
var websocketConnection: any;

async function main() {
    var port = '1978' 
    var localClientUrl = "ws://127.0.0.1:" + port;

    // Set up and handle websocket connection to our desktop client.
    websocketConnection = await connectWebsocket(localClientUrl).then(function (c) {
        return c;
    }).catch(function (err) {
        console.log("Websocket connection error. Is the client running with <pre>--connection-type WebSocket</pre> on port " + port + "?");
        console.log(err);
    })

    websocketConnection.onmessage = function (e : any) {
        handleResponse(e);
    };

    sendSelfAddressRequest();
}

// Handle any messages that come back down the websocket. 
function handleResponse(responseMessageEvent : MessageEvent) {

    try {
            let response = JSON.parse(responseMessageEvent.data.toString());
        if (response.type == "error") {
            console.log("\x1b[91mAn error occured: " + response.message + "\x1b[0m")
        } else if (response.type == "selfAddress") {
            ourAddress = response.address;
            console.log("\x1b[94mOur address is: " + ourAddress + "\x1b[0m")
        } else if (response.type == "received") {
            let messageContent = JSON.parse(response.message)

            console.log('\x1b[93mRecieved : \x1b[0m');
            console.log('\x1b[92mName : ' + messageContent.name + '\x1b[0m');
            console.log('\x1b[92mService : ' + messageContent.service + '\x1b[0m');
            console.log('\x1b[92mComment : ' + messageContent.comment + '\x1b[0m');

            console.log('\x1b[93mSending response back to client... \x1b[0m')

	    sendMessageToMixnet(response.senderTag)
        }
    } catch (_) {
        console.log('something went wrong in handleResponse')
    }
}

function sendMessageToMixnet(senderTag: string) {

    // Place each of the form values into a single object to be sent.
    const messageContentToSend = {
        text: 'We recieved your request - this reply sent to you anonymously with SURBs',
        fromAddress : ourAddress
    }
    
    const message = {
        type: "reply",
        message: JSON.stringify(messageContentToSend),
    	senderTag: senderTag
    }
    
    // Send our message object via out via our websocket connection.
    websocketConnection.send(JSON.stringify(message));
}

// Send a message to the mixnet client, asking what our own address is. 
function sendSelfAddressRequest() {
    var selfAddress = {
        type: "selfAddress"
    }
    websocketConnection.send(JSON.stringify(selfAddress));
}

// Function that connects our application to the mixnet Websocket. We want to call this first in our main function.
function connectWebsocket(url : string) {
    return new Promise(function (resolve, reject) {
        var server = new WebSocket(url);
        console.log('connecting to Mixnet Websocket (Nym Client)...')
        server.onopen = function () {
            resolve(server);
        };
        server.onerror = function (err) {
            reject(err);
        };

    });
}

main();

main() is still the function in charge of initializing and executing your application. Note that the SP’s client is running on port 1978. This is so we don’t have a conflict with the client we have running for the UC part of our tutorial code!

handleResponse() works in a similar manner as the function with the same name in simple-service-provider/user-client/src/index.ts. This implementation logs to the console instead of passing messages to a UI.

The \x1b prefix you see in console.log adds colour to console ouput. The number that you see following the [ and preceeding m is the colour code that can be compared here. Its a nice and quick way of styling our terminal output.

When the SP receives a message from the mixnet, sendMessageToMixnet() sends a response back to notify the the user that the SP recieved their request successfully.

Connecting to your Nym Client

Remember to init and run this client using port 1978 to avoid port clashes.

  • Refresh your browser window. You should see a successful response, including a Nym address, in your console:
> service-provider@1.0.0 start:dev
> nodemon

[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): src/**/*
[nodemon] watching extensions: ts,js
[nodemon] starting `ts-node ./src/index.ts`
connecting to Mixnet Websocket (Nym Client)...
Our address is: 6V5eEguz4rUsfntVLKQuD2ymgdY5iDKCV2GY2EH3CxG4.AKdk22atwRaVkN2PLEDsWUKKDc3ieNm1avKqVGgmJx8s@FQon7UwF5knbUr2jf6jHhmNLbJnMreck1eUcVH59kxYE

Set Service Address in User Client

The final step of this tutorial is to update our User Client code with the address of the now-running Service Provider so it can send it a message.

Copy the SP’s Nym address from your console and set it as the value of the targetAddress variable on line 2 of simple-service-provider/user-client/src/index.ts.

var targetAddress: string = '6V5eEguz4rUsfntVLKQuD2ymgdY5iDKCV2GY2EH3CxG4.AKdk22atwRaVkN2PLEDsWUKKDc3ieNm1avKqVGgmJx8s@FQon7UwF5knbUr2jf6jHhmNLbJnMreck1eUcVH59kxYE';
Last change: 2023-09-19, commit: 8aa8f07c

Sending a Message Through the Mixnet

You are now ready to send a message through the mixnet!

You should have the following set up:

  • A User Client Web App
  • A Service Provider backend service
  • 2 Nym websocket clients, one for each component above

Simply fill in the fields in your browser and click Send. In your browser you will see the message sent to the SP, followed by the response from the SP. In your console you will see the logged incoming message from the UC, and the reply message to itfrom the SP.

This small project can be used as a template to start conceptualizing and developing more complex PEApps. Stay tuned for more soon, and if you’re searching for inspiration check out the community apps list!

Last change: 2023-09-19, commit: 8aa8f07c

Nym General FAQ

If you have questions which are not answered below, please share them at our DevRel AMAs or seek support in Nym’s community

What is Nym?

Nym is a privacy platform that secures user data and protects against surveillance at the network level.

The platform does so by leveraging different technological components:

  • Nym Mixnet, a type of overlay network that makes both content and metadata of transactions private through mixing, network-level obfuscation and incentivisation (using Sphinx);
  • A blockchain called Nyx, our Cosmos SDK blockchain, to allow for us to use payment tokens in the form of NYM, as well as smart contracts, in order to create a robust, decentralized, and secure environment incentives for the Mixnet;
  • Coconut, a zero-knowledge signature scheme, that creates an application-level private access control layer to power Zk-Nyms;
  • A utility token NYM, to pay for usage, measure reputation and serve as rewards for the privacy infrastructure.

Simply put, the Nym network (“Nym”) is a decentralized and incentivized infrastructure to provision privacy to a broad range of message-based applications and services. Think of it as a “Layer 0” privacy infrastructure for the entire internet.

Related articles:

What’s the difference between Nym and VPNs?

Nym is not an onion routing system, it is not a decentralized VPN - it’s much more than that. Nym is a mixnet meant to stop precisely the traffic analysis attacks that Tor and dVPNs are vulnerable to.

It is an orthogonal design that maintains better privacy and can support anonymity, although usually with a cost in terms of latency. It basically is an infrastructure on which privacy preserving apps can be built, leveraging the Mixnet and Coconut credentials, amongst others. The Nym mixnet and VPNs differ because VPNs do not mix nor do they protect metadata from an adversary who may be able to watch the entire network.

Related articles:

What is Nym’s VPN?

Since Q2 2023 the Nym core team has been working on launching the first major consumer facing product that runs on top of the Nym mixnet: a high speed, trustless and decentralized VPN, paid for via the NYM token - facilitating anonymous payments if wished. The product positions itself as a full-network protection service available across all of a user’s devices, leveraging the Nym Mixnet and other primitives to offer split tunneling and traffic obfuscation techniques to protect against censorship.

Related articles:

Last change: 2023-09-19, commit: 8aa8f07c

Integrations FAQ

On this page, you’ll find links and frequently asked questions on how to get started on integrating your project with Nym’s Mixnet and its blockchain, Nyx.

General Info

Codebase Info

Documentation Info

  • Documentation
  • Developer Portal - you are currently viewing the Developer Portal

Wallet Installation

The Nym wallet can be downloaded here.

You can find all the instructions related to setting up your wallet in the docs, as well as instructions on how to build the wallet if there is not a downloadable version built for your operating system.

What are the machine hardware requirements for Nym Wallet?

About 16GB of RAM is recommended for the wallet. However you can expect an average memory usage of ~100MB.

Interacting with the Nyx blockchain

Where can I find information on the blockchain, such as RPC endpoints?

You can find most information required for integration in the Cosmos Chain Registry and Keplr Chain Registry repositories.

How can I use JSON-RPC methods to interact with the Nyx blockchain?

There are multiple ways to use JSON-RPC methods to interact with the Nyx blockchain. Which method you use will depend on the type of application you are integrating Nyx interactions into.

  1. The standalone nyxd binary can be used for CLI wallets, interacting with smart contracts via the CLI, setting up RPC nodes, and even running validators. This is a version of the Cosmos Hub’s gaiad binary compiled with Nyx chain configuration, and is written in Go. Instructions on setting up the nyxd binary can be found here. This is recommended for more complex commands. For full documentation check the gaiad documentation.

  2. CosmJS is a Typescript library allowing for developers to interact with CosmosSDK blockchains from a Javascript or Typescript project. You can find it on Github here and an explainer of its functionality in the Cosmos Developer Portal. You can find a list of example apps which use CosmJS here.

  3. The Nym-CLI tool, a standalone rust binary which can be built and used according to the docs can be used in much the same way as nyxd. It is a bit simpler to use than the nyxd binary, but is not recommended for complex queries, and not all commands are currently implemented. A list of Nym CLI commands and example usage can be found here

How do I generate an address/mnemonic for users to interact with?

Nyxd

Use the following command, replacing your_id with the ID you want to use for your keypair:

./nyxd keys add your_id --chain-id=nyx --gas=auto --gas-adjustment=1.4 --fees=7000unym

Nym-CLI

./nym-cli account create

Both methods will generate a keypair and log the mnemonic in the console.

CosmJS

You can find example code for keypair generation here.

How to get block information like block height, block hash, block time as so on?

Nyxd

You would use one of the subcommands returned by this command:

./nyxd query tx --chain-id=nyx --gas=auto --gas-adjustment=1.4 --fees=7000unym

Nym-CLI

./nym-cli block current-height

CosmJS

CosmJS documentation can be found here. We will be working on example code blocks soon.

How to get account/address balance to check there is enough coins to withdraw?

Nyxd

./nyxd query bank balances <address> --chain-id=nyx --gas=auto --gas-adjustment=1.4 --fees=7000unym

Nym-CLI

./nym-cli account balance

CosmJS

CosmJS documentation can be found here. We will be working on example code blocks soon.

How do I transfer tokens to another address?

Nyxd

./nyxd tx bank send [from_key_or_address] [to_address] [amount] --chain-id=nyx --gas=auto --gas-adjustment=1.4 --fees=7000unym

Nym-CLI

./nym-cli account send TARGET_ADDRESS AMOUNT

CosmJS

CosmJS documentation can be found here. We will be working on example code blocks soon.

Does the address support the inclusion of a memo or destinationTag when doing the transfer?

Yes, it is supported.

Can I use my Ledger hardware wallet to interact with the Nyx blockchain?

Yes. Follow the instructions in the Ledger support for Nyx documentation.

Where can I find network details such as deployed smart contract addresses?

In the network defaults file.

NYM Token

The token used to reward mixnet infrastructure operators - NYM - is one of the native tokens of the Nyx blockchain. The other token is NYX.

NYM is used to incentivise the mixnet, whereas NYX is used to secure the Nyx blockchain via Validator staking.

Integration with Nym’s technology stack will most likely involve using NYM if you do need to interact with the Nyx blockchain and transfer tokens.

I’ve seen an ERC20 representation of NYM on Ethereum - what’s this and how do I use it?

We use the Gravity Bridge blockchain to bridge an ERC20 representation of NYM between the Cosmos ecosystem of IBC-enabled chains and Ethereum mainnet. Gravity Bridge is its own IBC-enabled CosmosSDK chain, which interacts with a smart contract deployed on Ethereum mainnet.

The ERC20 representation of NYM cannot be used with the mixnet; only the native Cosmos representation is usable for staking or bonding nodes.

If you need to transfer tokens across the bridge, we recommend users use Cosmostation’s spacestation.zone dApp with Metamask and Keplr.

What is Circulating Supply and how to find out the distribution amount?

Circulating supply is the total number of available NYM. NYM is currently present on the IBC-enabled Nyx blockchain, as well as in ERC20 form on Ethereum Mainnet.

The Validator API endpoints can be found via the Swagger Documentation. The following endpoints can be called to retrieve the correct distribution amount and circulating supply within Nym.

Using this API endpoint returns information about the circulating supply of Nym tokens:

/circulating-supply

Query Response:

{
    "total_supply": {
        "denom": "unym",
        "amount": "1000000000000000"
    },
    "mixmining_reserve": {
        "denom": "unym",
        "amount": "241105338883248"
    },
    "vesting_tokens": {
        "denom": "unym",
        "amount": "390255200928865"
    },
    "circulating_supply": {
        "denom": "unym",
        "amount": "368639460187887"
    }
}
  • total_supply- The total number of NYM tokens that have been created and can exist, including those that are currently in circulation and those that are reserved for various purposes.

  • mixmining_reserved- The number of NYM tokens that are reserved for the mixnet miners who help to power the Nym network.

  • vesting_tokens- The number of NYM tokens that are subject to vesting, meaning they are gradually released over time to certain stakeholders such as the team, advisors, and early investors.

  • circulating_supply- The number of NYM tokens that are currently in circulation and available to be traded on the open market, which is calculated by subtracting the mixmining_reserved and vesting_tokens from the total_supply.

Using this API endpoint returns the current value of the total supply of NYM tokens:

/circulating-supply/total-supply-value

Query Response:

1000000000.0 

The maximum number of NYM tokens that can ever be created is 1 billion.

Using this API endpoint returns the current value of the circulating supply of NYM tokens:

/circulating-supply/circulating-supply-value

Query Response:

368639460.187887

This refers to the present quantity of NYM tokens that are actively in circulation.

Sending traffic through the Nym mixnet

Is the mixnet free to use?

For the moment then yes, the mixnet is free to use. There are no limits on the amount of traffic that an app can send through the mixnet.

Do I need to run my own gateway to send application traffic through the mixnet?

No, although we do recommend that apps that wish to integrate look into running some of their own infrastructure such as gateways in order to assure uptime.

How can I find out if an application is already supported by network requester services?

You can check the default allowed list file to see which application traffic is whitelisted by default. If the domain is present on that list, it means that existing network requesters can be used to privacy-protect your application traffic. Simply use NymConnect to connect to this service through the mixnet.

Last change: 2023-09-19, commit: 8aa8f07c

Rewards FAQ

On this page you will find important information about participation in community activities which involve NYM token rewards, such as contests, giveaways, and promotions. Before participating in any such activity, make sure to read this page carefully.

Am I eligible to participate? What are the conditions?

Participation in any program or community activity involving NYM token rewards is only available to individuals who do not reside in locations that makes them ineligible to participate and/or receive prizes, including:

The USA, Central African Republic, Cuba, Iran, Iraq, Lebanon, Libya, North Korea, Somalia, South Sudan, Sudan, Syria, Venezuela, Yemen, and Russian occupied regions of Ukraine (including Crimea, and parts of Donetsk and Luhansk).

Be nice and play fair! Nym reserves the right to determine the final outcome of its programs and community activities and disqualify any participants without prior notice or recourse for any reason, including fraudulent behavior and failure to adhere to the Nym Code of Conduct.

I won NYM rewards. How can I claim them?

First things first: congratulations! We hope you had some fun in the process. A Nym Community Manger will tag you on Telegram or Discord - send them a private message to start the claim process. You will receive your unique reward ID and detailed instructions on how to use it to claim your rewards. Remember: we never DM you first!

Note: you have 2 weeks to claim your rewards! If we don’t hear from you, your reward ID will expire.

Do I need to pass KYC?

Before receiving NYM token rewards, you are required to successfully complete KYC verification with Synaps, our KYC provider. You only need to do this once!

This is my first time - How do I pass KYC?

To claim your rewards, you will be asked to provide your email address. If you haven’t passed KYC yet, your email address will be added to our KYC whitelist and you will receive a welcome email from Synaps with instructions on how to start the KYC process. Once you complete KYC and provide a valid Nym wallet address, your rewards will be transferred to your wallet automatically.

Note: it may take up to 2 weeks for your email address to get whitelisted. Be patient and keep an eye on your inbox!

To receive your tokens, you will need a Nym wallet address. If you don’t already have one, download the Nym wallet and create an account. You can copy your wallet address from the “receive” tab of the wallet. Make sure to keep your mnemonic safe, as losing that means losing your tokens.

Tip: If you are not sure you have completed Nym KYC before, visit our KYC portal on Synaps and try to log in with your email.

I have already passed KYC on Synaps. What do I need to do next?

If you have successfully completed KYC on Synaps earlier and provided your Nym wallet address, you are all set - we will send you a link so you can claim your rewards.

Note: after claiming your NYM rewards, it may take up to 3 weeks for the tokens to be transferred to your wallet. Be patient and keep an eye on your wallet!

I received my NYM tokens. What can I do with them?

We’re glad you asked! The Nym network is the most robust privacy infrastructure the world has ever seen, and the NYM token is how you can access its utility:

  • As a user, unparalleled privacy for your communications
  • As a mix node operator, rewards for your crucial role in operating our decentralized privacy infrastructure
  • As a delegator, rewards for securing the Nym network and ensuring outstanding quality of service

Using the Nym network is free for now, but user fees will be introduced in the future and those will be paid in NYM tokens, so set them aside! And until then, you can contribute to running our decentralized infrastructure and earn mix mining rewards by setting up Nym mix nodes and bonding your tokens to them. You don’t need to run nodes to contribute to the security and performance of the Nym network though. You can pledge your trust in someone else’s node by delegating your NYM tokens to it, and receive a share of their mix mining rewards.

You can find out more about how staking works in this video from our Chief Scientist Claudia Diaz and if you need help setting up your mix node, choosing one to delegate your tokens to, or anything else, our community is there to help on Discord and Telegram.

Last change: 2023-09-19, commit: 8aa8f07c

DevRel AMAs

The Nym Technology Developer Relations AMA (Ask Me Anything) is an event organised by the Nym team to engage and connect with the developer community as well as answer any questions that they may have.

⚠️ Hosted every fortnight on Wednesday @ 4pm UTC

Archives of these calls can be found here.

Last change: 2023-09-19, commit: 8aa8f07c

Community Applications

We love seeing our developer community create applications using Nym. If you would like to share your application with the community, please submit a pull request to the main branch of the nymtech/dev-portal repository.

Pastenym

A pastebin inspired project, offer a solution for sharing text with Nym products to offer full anonymity, even on metadata level.

Nostr-Nym

Nostr-nym offer a solution to use Nostr protocol by giving the possibility to run a relay on mixnet. By using a nostr client compatible with the mixnet, users can protect their privacy to be able to use Nostr has the want, without being observed.

Spook

Ethereum RPC request mixer uses the Nym network mixing service to anonymize RPC requests to the Ethereum network without revealing sensitive data and metadata.

Ethereum Transaction Broadcaster

Ethereum Transaction Broadcaster that uses the Nym Mixnet to provide privacy and anonymity for transactions on the Ethereum network command-line interface.

NymDrive

An open-source, decentralized, E2E encrypted, privacy friendly alternative to Google Drive/Dropbox, allowing for file encryption and decryption using the Nym Mixnet.

Nym Dashboard

Developed by No Trust Verify, this dashboard is a great tool to get information about the mixnet, gateways and mixnodes.

Is Nym Up

Explore whether we’re up through IsNymUp, a tool that helps check the heath of the Nym network as well as some mixnet related statistics!

DarkFi over Nym

DarkFi leverages Nym’s mixnet as a pluggable transport for IRCD, their p2p IRC variant. Users can anonymously connect to peers over the network, ensuring secure and private communication within the DarkFi ecosystem.

Nymstr email

Experience secure and private email communication with ease using Nymstr email, which enables seamless transmission of emails over a SOCKS5 proxy and our NYM mixnet!

Minibolt

Proxies the clearnet connections of a Bitcoin node and its associated tools using the NYM mixnet.

NymGraph

NymGraph is a graphical chat client for Nym running on Ubuntu and Debian. Test it to chat over the Nym network!



Community Guides

We aren’t the only ones writing documentation: the Nym developer community is also a great source of guides and resources, some of which we’ve included here.

No Trust Verify

No Trust Verify is a project that aims to build open-source, privacy-enhancing technologies that make it easier to use the Internet securely and anonymously. Their focus is on providing tools and services that make it simple for developers to create decentralized applications (dApps) that respect users’ privacy.

The Way of the NYMJA

by Pineapple Proxy🍍

Born out of a study group from Nym’s Shipyard Academy, Pineapple Proxy has emerged as a cluster of motivated and skilled individuals who see the new internet taking shape. With vibecare at the heart of their approach, this zesty collective is on a mission to make privacy convenient for everyone via content, new tools, events, and novel experiences. They believe in collective intelligence, empathy, and collaboration as the means by which privacy will become a meaningful reality.

Last change: 2023-09-19, commit: 8aa8f07c

Change Service Grantee Information

If you wish to update any of the following information:

  • Email Address
  • NYM address
  • Network Requester
  • Gateway ID Key
  • Gateway Address
  • Service
  • Payment Address

Please reach out either in the #service-grantees channel on Discord or on Matrix.

Last change: 2023-09-19, commit: 8aa8f07c

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.

Last change: 2023-09-19, commit: 8aa8f07c

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.

Last change: 2023-09-19, commit: 8aa8f07c