Skip to content

syscoin/sysnode-backend

Repository files navigation

sysnode-backend

Express API behind sysnode.info. Aggregates Syscoin Core RPC + Blockbook + masternode telemetry + a small authenticated subsystem for governance proposal drafts, vote reminders, and "Pay-with-Pali" collateral flows.

The public dashboard is served by sysnode-info; this repo is the backend half of that stack.

Runtime surface

Public, unauthenticated routes (cached, read-only):

  • /mnstats, /masternodes, /mnlist, /mnsearch — masternode data
  • /governance — active and historical governance proposals
  • /csvparser — CSV-ingest helper used by the dashboard

Authenticated routes (cookie + CSRF, same-site):

  • /auth/* — registration, verification, login, session, delete account
  • /vault/* — encrypted per-user blobs (notification prefs, proposal drafts)
  • /gov/proposals/* — governance proposal wizard, submissions, collateral PSBT, vote receipts

Requirements

  • Node.js 20 LTS (engines in package.json gate Node ≥ 20, < 24)
  • A reachable syscoind on mainnet or testnet, with RPC enabled
  • SMTP server for verification emails and vote reminders (or MAIL_TRANSPORT=log for dry-run)
  • SQLite 3 (via better-sqlite3, no separate install; native module compiles at npm install)

Optional, used only for the Pali PSBT collateral path:

  • A Blockbook instance for the same network as the RPC node (https://blockbook.syscoin.org/ for mainnet, https://blockbook-dev.syscoin.org/ for testnet)

Local development

git clone https://github.com/syscoin/sysnode-backend.git
cd sysnode-backend
npm ci
cp .env.example .env        # then edit — see .env.example for inline docs
npm run dev                 # nodemon on :3001
npm test                    # full jest suite (~830 cases)

Configuration

All configuration is via environment variables. .env.example is the source of truth and carries inline rationale for every field. The short form:

Variable Purpose
PORT, BASE_URL Where the server listens, and the public URL baked into email links
CORS_ORIGIN, FRONTEND_URL SPA origin for credentialed CORS and verification-link base
TRUST_PROXY Reverse-proxy hop count (or CIDR) so req.ip is the real client
SYSNODE_DB_PATH Path to the SQLite file (auto-created)
SYSNODE_AUTH_PEPPER 32-byte hex secret; required in production
SMTP_*, MAIL_FROM, MAIL_TRANSPORT Mail delivery; MAIL_TRANSPORT=log prints to stdout
SYSCOIN_RPC_HOST, SYSCOIN_RPC_PORT RPC endpoint (default 127.0.0.1:8370)
SYSCOIN_RPC_COOKIE_PATH Preferred — absolute path to Core's .cookie for same-host deployments
SYSCOIN_RPC_USER, SYSCOIN_RPC_PASS Fallback static creds for remote RPC nodes
SYSCOIN_NETWORK, SYSCOIN_BLOCKBOOK_URL Enables the Pay-with-Pali collateral PSBT path

Cookie vs static RPC auth

The backend supports both authentication modes and picks cookie over static when both are configured (with a one-line warning at boot). Cookie auth is zero-secret-management: syscoind rewrites the cookie on every restart, and the backend picks up the new token automatically via a 401-driven replay. Use it for any deployment where the backend runs on the same host as syscoind.

For remote RPC nodes, either configure rpcauth= in syscoin.conf and use the static SYSCOIN_RPC_USER / SYSCOIN_RPC_PASS here, or mount the cookie file via a secure channel.

Full-stack test deployment (single host)

These steps stand up sysnode-backend + sysnode-info on one Ubuntu box that already runs syscoind. HTTP-only; intended for staging and testing, not production. Everything installs into the user's home directory — no sudo required on most steps (a couple of optional hardening steps do need it; they are clearly marked).

Walked end-to-end against Ubuntu 24.04 LTS + Node 22. Port layout: backend :3001, frontend :3000, Mailpit UI :8025, Mailpit SMTP :1025 (loopback-only).

1. Node.js 20 or 22 + basic tooling

Node.js ≥ 20, < 24 (see engines in package.json). If the box already has a Node in that range, skip the nvm block. Ubuntu 24.04 ships a compatible Node in its default repos; many one-click images come with Node 20 or 22 pre-installed.

# Only if you don't already have Node 20–23.x
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 22 && nvm use 22

# Use a user-local npm prefix so global installs don't need sudo
mkdir -p ~/.npm-global ~/.local/bin
npm config set prefix ~/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$HOME/.local/bin:$PATH"' >> ~/.bashrc
export PATH="$HOME/.npm-global/bin:$HOME/.local/bin:$PATH"

npm install -g pm2 serve

2. Mailpit (open-source SMTP catcher + web UI)

Mailpit is a single-binary SMTP sink that exposes every delivered message through a local web UI. Perfect for staging: you click verification links out of the inbox instead of running a real mailer. We install it into ~/.local/bin (no sudo) and supervise it with pm2 alongside the two Node processes.

cd /tmp
ARCH=$(uname -m); case "$ARCH" in x86_64) MP=linux-amd64;; aarch64) MP=linux-arm64;; esac
curl -fsSL "https://github.com/axllent/mailpit/releases/latest/download/mailpit-${MP}.tar.gz" -o mailpit.tgz
tar -xzf mailpit.tgz
mv mailpit ~/.local/bin/mailpit && chmod +x ~/.local/bin/mailpit

pm2 start "mailpit --smtp 127.0.0.1:1025 --listen 0.0.0.0:8025" --name mailpit

After this, http://<server-ip>:8025 is the inbox.

3. Clone both repos

mkdir -p ~/apps && cd ~/apps
git clone https://github.com/syscoin/sysnode-backend.git
git clone https://github.com/syscoin/sysnode-info.git

4. Configure the backend

cd ~/apps/sysnode-backend
npm ci

# Locate the Core cookie (path depends on the user that runs syscoind)
ls -l ~/.syscoin/.cookie 2>/dev/null || sudo ls -l /root/.syscoin/.cookie

PEPPER=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
COOKIE_PATH=/home/ubuntu/.syscoin/.cookie   # adjust to match the ls above
SERVER_IP=$(hostname -I | awk '{print $1}') # or hardcode the public IP

cat > .env <<EOF
PORT=3001
BASE_URL=http://${SERVER_IP}:3001
CORS_ORIGIN=http://${SERVER_IP}:3000
FRONTEND_URL=http://${SERVER_IP}:3000
NODE_ENV=development
TRUST_PROXY=loopback
SYSNODE_DB_PATH=./data/sysnode.db
SYSNODE_AUTH_PEPPER=${PEPPER}

# Mailpit — stdout-free, inbox visible at :8025
SMTP_HOST=127.0.0.1
SMTP_PORT=1025
SMTP_USER=
SMTP_PASS=
MAIL_FROM=no-reply@test.syscoin.dev
MAIL_TRANSPORT=smtp

# Syscoin Core RPC (cookie mode, preferred for same-host)
SYSCOIN_RPC_HOST=127.0.0.1
SYSCOIN_RPC_PORT=8370
SYSCOIN_RPC_COOKIE_PATH=${COOKIE_PATH}
SYSCOIN_RPC_LOG_LEVEL=error

# Pay-with-Pali (mainnet)
SYSCOIN_NETWORK=mainnet
SYSCOIN_BLOCKBOOK_URL=https://blockbook.syscoin.org/
EOF

mkdir -p data

The backend does not load .env automatically. It has no dotenv dependency. We load the file with Node's native --env-file= flag (Node 20.6+), which is why every invocation of node for this repo below passes --env-file=.env.

Cookie file permissions. If the backend user can't read ~/.syscoin/.cookie (different uid than syscoind), add rpccookieperms=group to ~/.syscoin/syscoin.conf and restart syscoind, then add the backend's user to the syscoin group. The backend's boot log prints the exact errno (ENOENT / EACCES) if it can't read the file.

Quick sanity check — confirms .env is loaded and RPC cookie auth works against Core:

node --env-file=.env -e '
  const { client, rpcServices } = require("./services/rpcClient");
  rpcServices(client.callRpc).getBlockchainInfo().call()
    .then(r => console.log(r.chain, r.blocks, "ibd=" + r.initialblockdownload))
    .catch(e => { console.error(e.message); process.exit(1); });
'
# expected: "main <height> ibd=false"

5. Build and serve the frontend

REACT_APP_API_BASE is a Create React App build-time variable — it must be set before npm run build or the bundle will keep pointing at the default.

cd ~/apps/sysnode-info
npm ci
SERVER_IP=$(hostname -I | awk '{print $1}')
REACT_APP_API_BASE=http://${SERVER_IP}:3001 npm run build

6. Start both under pm2

cd ~/apps/sysnode-backend
pm2 start "node --env-file=.env server.js" --name sysnode-backend

cd ~/apps/sysnode-info
pm2 start "serve -s build -l 3000" --name sysnode-info

pm2 save
pm2 list

Optional — survive a full reboot. Requires sudo; skip if you don't have it and just run pm2 resurrect after any reboot:

pm2 startup systemd -u $USER --hp $HOME   # prints one sudo line; paste it

7. Firewall (optional)

If ufw isn't active on the host, your cloud security-group / network-layer rules are what matter — adjust those instead.

sudo ufw status
# If active:
sudo ufw allow 3000/tcp   # frontend
sudo ufw allow 3001/tcp   # backend API
sudo ufw allow 8025/tcp   # Mailpit UI
# DO NOT open 1025 (SMTP) — keep it loopback-only

8. Smoke-test end to end

# Backend reachable + RPC cookie auth working (real stats from Core)
curl -s http://<server-ip>:3001/mnstats | head -c 200

# Mail pipeline. Open a shell on the server and run:
cd ~/apps/sysnode-backend
node --env-file=.env -e '
  const { createMailer } = require("./lib/mailer");
  createMailer({ transport: "smtp" }).sendVerification({
    to: "smoketest@example.com",
    link: process.env.BASE_URL + "/auth/verify?t=smoketest"
  }).then(() => console.log("sent"));
'
# Then: curl -s http://<server-ip>:8025/api/v1/messages | head -c 400
# You should see one message with subject "Verify your Syscoin Sysnode account".

Then exercise the UI:

  1. Open http://<server-ip>:3000 — dashboard loads.
  2. Register a user in the UI → open http://<server-ip>:8025, click the verification link from the inbox.
  3. Go into the governance proposal wizard — the Pay with Pali button should be enabled (assuming your browser has Pali installed and the chain guard verified mainnet).

Updating the stack

pm2 stop sysnode-backend sysnode-info

cd ~/apps/sysnode-backend
git pull && npm ci

cd ~/apps/sysnode-info
git pull && npm ci
REACT_APP_API_BASE=http://<server-ip>:3001 npm run build

pm2 restart sysnode-backend sysnode-info

Troubleshooting

Symptom Likely cause Check
Backend exits at boot, failed to read rpc cookie at ... ENOENT Wrong SYSCOIN_RPC_COOKIE_PATH sudo -u <syscoind-user> cat <path>
Backend exits at boot, ... EACCES Backend user can't read the cookie Use rpccookieperms=group + usermod -aG
Backend rejects RPC with 401 after a Core restart once, then recovers Expected — cookie rotated, backend replayed with the new one No action
Pay with Pali button disabled paliChainGuard reports pali_path_chain_mismatch or pali_path_rpc_down GET /gov/proposals/network returns a paliPathReason
Verification emails never arrive MAIL_TRANSPORT=smtp but Mailpit isn't running systemctl status mailpit
Frontend hits https://syscoin.dev instead of the test backend REACT_APP_API_BASE not set at build time Rebuild with the env var inline

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages