New: a threat-model-first guide to choosing your network defence, plus the nym-smoldvpn dVPN package and nym-swizzle sender hygiene.

Nym API Signer Setup

ℹ️

Our documentation often refers to syntax annotated in <> brackets. We use this expression for variables that are unique to each user (like path, local moniker, versions et cetera). Any syntax in <> brackets needs to be substituted with your correct name or version, without the <> brackets. If you are unsure, please check our table of essential parameters and variables (opens in a new tab).

This guide is for operators running a credential (zk-nym / ecash) signing nym-api on mainnet, with a local nyxd RPC on the same machine, a public HTTPS announce address fronted by nginx and Let's Encrypt, and nym-api bound to loopback only.

ℹ️

At present our mainnet operates with a select group of reputed validators and signers, and we are not accepting new operators at this time. We do extend the set from time to time - if you are interested, get in touch with the Nym team. Any updates to this policy will be announced.

⚠️

Complete the local RPC setup first. Your nyxd must be synced (catching_up returns false) before you initialise the API.

For background on what the Nym API does, how rewards work and the hardware it needs, see the Nym API page.

Port reference

PortBindPurpose
8000127.0.0.1 onlynym-api HTTP, never expose publicly
800.0.0.0HTTP for ACME challenge and redirect to HTTPS
4430.0.0.0HTTPS reverse proxy to nym-api

Prerequisites

ItemRequirement
HostUbuntu 24.04 x86_64, same machine as the local nyxd
Local RPCnyxd active, catching_up == false, RPC at http://127.0.0.1:26657
DiskEnough for nyxd data plus 1-2 GB for nym-api and its sqlite database
DNSAn A/AAAA record for your <FQDN> pointing at this host, in place before running certbot
AccountA dedicated Nyx account mnemonic funded with at least 100 NYM
BinaryCurrent network nym-api release
ℹ️

Membership in the ecash cw4 group contract is controlled by the network. Finishing this guide makes your API ready to sign, it does not automatically make you a dealer. Group addition only takes effect at the next DKG ceremony.

Setup

1. Install prerequisites
apt update
apt install -y ca-certificates curl wget jq sqlite3 nginx certbot python3-certbot-nginx
2. Install nym-api and nym-cli

Check the Nym binary releases (opens in a new tab) for the current version and download the binaries:

export NYM_API_TAG="<CURRENT_RELEASE_TAG>"
 
cd /root
 
curl -fsSL -o nym-api \
  "https://github.com/nymtech/nym/releases/download/${NYM_API_TAG}/nym-api"
curl -fsSL -o nym-cli \
  "https://github.com/nymtech/nym/releases/download/${NYM_API_TAG}/nym-cli"
 
chmod +x nym-api nym-cli
  • Verify both run:
./nym-api --version
./nym-cli --version
⚠️

Do not proceed if a binary will not run - missing libraries, wrong architecture or an empty download will all fail here rather than later.

3. Fund the signer account

Create a dedicated wallet for this signer, do not reuse an existing one, and fund it with at least 100 NYM.

export NYM_API_ACCOUNT_ADDRESS=<YOUR_ACCOUNT_ADDRESS>
 
./nym-cli account balance $NYM_API_ACCOUNT_ADDRESS
⚠️

nym-api refuses to start as a signer with less than 100 NYM (100000000unym) on the account, because it needs to cover verification fees. Monitor the balance and top it up as needed.

4. Initialise the API

Pick the public HTTPS origin that clients and other signers will dial. It must use https:// and resolve to this host.

export FQDN="<YOUR_FQDN>"
export ANNOUNCE_ADDRESS="https://${FQDN}"
export NYM_API_ACCOUNT_MNEMONIC="<YOUR_MNEMONIC>"
 
./nym-api init \
  --announce-address "${ANNOUNCE_ADDRESS}" \
  --mnemonic "${NYM_API_ACCOUNT_MNEMONIC}" \
  --nyxd-validator 'http://localhost:26657/' \
  --enable-zk-nym
  • Then clear the mnemonic from your shell so it doesn't linger in history:
unset NYM_API_ACCOUNT_MNEMONIC
  • Verify the config was created:
test -f "/root/.nym/nym-api/default/config/config.toml" && echo "config ok"
⚠️

If init fails halfway, do not re-run it blindly - init refuses to overwrite an existing config. Back up any keys first, then remove or rename /root/.nym/nym-api/default/.

5. Check config.toml

Open /root/.nym/nym-api/default/config/config.toml and confirm these values:

# under [base]
local_validator = 'http://localhost:26657/'
bind_address = '127.0.0.1:8000'
# under [ecash_signer]
enabled = true
announce_address = 'https://<FQDN>'
⚠️

Do not enable [network_monitor] or [rewarding] on a signer.

6. Run nym-api as a systemd service
  • Create /etc/systemd/system/nym-api.service:
[Unit]
Description=Nym API
StartLimitInterval=350
StartLimitBurst=10
 
[Service]
User=root
Type=simple
ExecStart=/root/nym-api run
Restart=on-failure
RestartSec=30
LimitNOFILE=65536
 
[Install]
WantedBy=multi-user.target
  • Then start it:
systemctl daemon-reload
systemctl enable nym-api
systemctl start nym-api
systemctl status nym-api --no-pager
journalctl -u nym-api -f
  • Loopback must accept connections:
curl -fsS http://127.0.0.1:8000/v1/ecash/signer-status | jq .

Expect a body containing:

"signerDisabled": false,
"isEcashSigner": true,
"hasSigningKeys": true

If the process exits immediately, check the account balance is at least 100 NYM, that nyxd is reachable and not catching up, that the mnemonic in config.toml is correct and not empty, and that [ecash_signer] enabled is true with an announce_address set.

7. Configure nginx and TLS

Your DNS record for <FQDN> must already point at this host before running certbot.

export FQDN="<YOUR_FQDN>"
 
rm -f /etc/nginx/sites-enabled/default
 
tee "/etc/nginx/sites-available/${FQDN}" >/dev/null <<EOF
server {
  listen 80;
  listen [::]:80;
  server_name ${FQDN};
  add_header 'Access-Control-Allow-Origin' '*';
 
  location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header Host \$host;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
  }
}
EOF
 
ln -sfn "/etc/nginx/sites-available/${FQDN}" "/etc/nginx/sites-enabled/${FQDN}"
nginx -t
systemctl enable nginx
systemctl reload nginx
  • Get a certificate:
certbot --nginx \
  -d "${FQDN}" \
  -m "<YOUR_EMAIL>" \
  --agree-tos \
  --non-interactive \
  --redirect
  • Confirm renewal is scheduled:
systemctl enable --now certbot.timer
certbot renew --dry-run

Health checks

# 1. local chain still synced
curl -s http://127.0.0.1:26657/status | jq '.result.sync_info.catching_up'
 
# 2. nym-api process
systemctl is-active nym-api
 
# 3. loopback signer status
curl -fsS http://127.0.0.1:8000/v1/ecash/signer-status | jq '{
  epoch: .body.dkgEcashEpochId,
  disabled: .body.signerDisabled,
  is_signer: .body.isEcashSigner,
  has_keys: .body.hasSigningKeys
}'
 
# 4. public announce address
curl -fsS "https://${FQDN}/v1/ecash/signer-status" | jq '.body.dkgEcashEpochId'
 
# 5. confirm the RPC in use is local
curl -fsS http://127.0.0.1:8000/v1/network/chain-status | jq .
CheckExpected
signerDisabledfalse when [ecash_signer].enabled = true and the balance is at least 100 NYM
isEcashSignermay stay false until you are a registered dealer in a ceremony, that is normal
hasSigningKeystrue after a successful init
Public URLHTTP 200 with a dkgEcashEpochId field

If signerDisabled is true when you intended to sign, re-check [ecash_signer] enabled, the announce_address and the account balance.

After setup

A new signer is not yet a dealer. Before it can take part in credential issuance its account must be added to the cw4 group contract, which is the sole authority the DKG contract consults, and group changes only take effect at the next DKG ceremony.

⚠️

If you later change announce_address on a host that is already a registered dealer, let the Nym team know - editing config.toml alone does not update the chain.

Checklist

Before init:

  • Local RPC guide complete, catching_up returns false
  • nym-api and nym-cli installed and --version works
  • Signer account funded with at least 100 NYM
  • <FQDN> DNS already points at this host

After init, before public traffic:

  • local_validator = 'http://localhost:26657/'
  • bind_address = '127.0.0.1:8000'
  • [ecash_signer] enabled = true and announce_address = 'https://<FQDN>'
  • systemctl is-active nym-api returns active
  • curl http://127.0.0.1:8000/v1/ecash/signer-status returns 200

After nginx and TLS:

  • curl https://<FQDN>/v1/ecash/signer-status returns 200 with dkgEcashEpochId
  • Port 8000 is not open on the public interface
  • certbot.timer enabled and certbot renew --dry-run passes

Before relying on signing in production:

  • Account added to the cw4 group, arranged with the Nym team
  • Next DKG ceremony has run
  • isEcashSigner and signing behaviour confirmed with the Nym team