NodesTracker
Map live Dogecoin mainnet peers (full / pruned / light)
NodesTracker discovers Dogecoin mainnet peers from DNS seeds, completes P2P handshakes on port 22556, and shows who is live right now. The dashboard reports total/live/offline peers, live kind counts (full, pruned, light), and healthy full nodes on the agreed tip. Optional user/admin tokens gate peer APIs; hide_addresses can checksum public hostnames. Built from https://github.com/qlpqlp/nodestracker (v0.1.0). DogeBOX seeds settings.json on first boot under /storage/nodestracker. Not a wallet.
Latest version
v0.0.56
0.1.0
Review source
Use the tabs to switch between the manifest, Nix build file, and other text sources from the latest tag.
Latest release manifest (for manual review).
{
"manifestVersion": 1,
"meta": {
"name": "NodesTracker",
"version": "0.1.0",
"logoPath": "logo.png",
"shortDescription": "Map live Dogecoin mainnet peers (full / pruned / light)",
"longDescription": "NodesTracker discovers Dogecoin mainnet peers from DNS seeds, completes P2P handshakes on port 22556, and shows who is live right now. The dashboard reports total/live/offline peers, live kind counts (full, pruned, light), and healthy full nodes on the agreed tip. Optional user/admin tokens gate peer APIs; hide_addresses can checksum public hostnames. Built from https://github.com/qlpqlp/nodestracker (v0.1.0). DogeBOX seeds settings.json on first boot under /storage/nodestracker. Not a wallet.",
"upstreamVersions": {
"Go": "1.24",
"nodestracker": "v0.1.0"
}
},
"config": {
"sections": [
{
"name": "Web API",
"description": "HTTP listen port for the dashboard and JSON API (bound on 0.0.0.0 for DogeBox).",
"fields": [
{
"name": "PUBLIC_PORT",
"label": "HTTP Port",
"type": "text",
"default": "6969"
}
]
},
{
"name": "Access and Security",
"description": "Optional tokens gate /api/peers and admin routes. Empty tokens leave those routes open. Values seed first-boot settings.json.",
"fields": [
{
"name": "USER_TOKEN",
"label": "User token (peers / clients APIs)",
"type": "password",
"default": ""
},
{
"name": "ADMIN_TOKEN",
"label": "Admin token (settings, seeds, handshake)",
"type": "password",
"default": ""
},
{
"name": "HIDE_ADDRESSES",
"label": "Hide raw IPs in the public peer list",
"type": "toggle",
"default": false
}
]
},
{
"name": "Crawler",
"description": "How often to recheck peers, discover new addresses, and drop stale offline peers.",
"fields": [
{
"name": "CHECK_INTERVAL_MINUTES",
"label": "Peer recheck interval (minutes)",
"type": "text",
"default": "60"
},
{
"name": "DISCOVERY_MINUTES",
"label": "DNS discovery interval (minutes)",
"type": "text",
"default": "15"
},
{
"name": "STALE_DAYS",
"label": "Remove offline peers after N days",
"type": "text",
"default": "7"
},
{
"name": "DNS_SEEDS",
"label": "Optional DNS seeds (comma-separated; empty = defaults)",
"type": "text",
"default": ""
}
]
}
]
},
"container": {
"build": {
"nixFile": "pup.nix",
"nixFileSha256": "20e1d2138286a37c5909858639b091ca1c6eb36c4debec26f3a4cdb7e28543a3"
},
"services": [
{
"name": "nodestracker",
"command": {
"exec": "/bin/run.sh",
"cwd": "",
"env": null
}
}
],
"exposes": [
{
"name": "public-port",
"type": "http",
"port": 6969,
"interfaces": [
"nodestracker-public"
],
"listenOnHost": true,
"webUI": true
}
],
"requiresInternet": true
},
"interfaces": [
{
"name": "nodestracker-public",
"version": "0.1.0",
"permissionGroups": [
{
"name": "NodesTracker HTTP",
"description": "Web dashboard and JSON APIs for Dogecoin peer discovery",
"privilege": 1,
"routes": [
"/*"
],
"port": 6969
}
]
}
],
"dependencies": [],
"metrics": null
}
Build definition from the latest tag (filename from manifest).
# NodesTracker PUP for DogeBox / silly-pups.
# Service attr name MUST match manifest container.services[0].name ("nodestracker").
#
# Builds from https://github.com/qlpqlp/nodestracker via fetchurl (flat tarball hash).
# Keep tarball hash in sync with the pin, then recompute manifest.json nixFileSha256
# (LF SHA-256 of this file).
{ pkgs ? import <nixpkgs> {} }:
let
# Upstream release v0.1.0 → 6dae09e1448c0b2ee85df18d70d38c31cd6c5011
tag = "v0.1.0";
tarball = pkgs.fetchurl {
url = "https://github.com/qlpqlp/nodestracker/archive/refs/tags/${tag}.tar.gz";
hash = "sha256-2bj8lTgyxvrvVX9H9W1VmL5vHphJZmDHaM9z5nmEtRU=";
};
src = pkgs.runCommand "nodestracker-${tag}-src" {
nativeBuildInputs = [ pkgs.gnutar pkgs.gzip ];
} ''
mkdir -p $out
tar -xzf ${tarball} --strip-components=1 -C $out
# Align go directive with DogeBox toolchains (upstream may be older/newer).
sed -i 's/^go .*/go 1.24/' $out/go.mod
'';
nodestracker_bin = pkgs.buildGoModule {
pname = "nodestracker";
version = "0.1.0";
inherit src;
vendorHash = null;
go = pkgs.go_1_24;
nativeBuildInputs = [ pkgs.removeReferencesTo ];
# No third-party modules; strip Go store refs for the pup closure.
buildPhase = ''
export GOCACHE=$(pwd)/.gocache
export GO111MODULE=on
go build -trimpath -ldflags="-s -w" -o nodestracker .
'';
installPhase = ''
mkdir -p $out/bin
cp nodestracker $out/bin/
remove-references-to -t ${pkgs.go_1_24} $out/bin/nodestracker
'';
};
nodestracker = pkgs.writeShellScriptBin "run.sh" ''
set -e
PUBLIC_PORT="''${PUBLIC_PORT:-6969}"
CHECK_INTERVAL="''${CHECK_INTERVAL_MINUTES:-60}"
DISCOVERY_MINUTES="''${DISCOVERY_MINUTES:-15}"
STALE_DAYS="''${STALE_DAYS:-7}"
USER_TOKEN="''${USER_TOKEN:-}"
ADMIN_TOKEN="''${ADMIN_TOKEN:-}"
HIDE_RAW="''${HIDE_ADDRESSES:-0}"
SEEDS_RAW="''${DNS_SEEDS:-}"
STORAGE_DIR="/storage/nodestracker"
mkdir -p "$STORAGE_DIR"
HIDE_JSON="false"
case "$HIDE_RAW" in 1|true|TRUE|yes|YES) HIDE_JSON="true" ;; esac
if ! printf '%s' "$CHECK_INTERVAL" | grep -Eq '^[0-9]+$' || [ "$CHECK_INTERVAL" -lt 1 ]; then
CHECK_INTERVAL="60"
fi
if ! printf '%s' "$DISCOVERY_MINUTES" | grep -Eq '^[0-9]+$' || [ "$DISCOVERY_MINUTES" -lt 1 ]; then
DISCOVERY_MINUTES="15"
fi
if ! printf '%s' "$STALE_DAYS" | grep -Eq '^[0-9]+$' || [ "$STALE_DAYS" -lt 1 ]; then
STALE_DAYS="7"
fi
if [ ! -f "$STORAGE_DIR/settings.json" ]; then
_json_escape() {
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
}
USER_TOKEN_JSON=$(_json_escape "$USER_TOKEN")
ADMIN_TOKEN_JSON=$(_json_escape "$ADMIN_TOKEN")
SEEDS_JSON='["seed.dogecoin.org","seed.multidoge.org","seed2.multidoge.org"]'
_seeds_flat=$(printf '%s' "$SEEDS_RAW" | tr '\n' ',')
if [ -n "$(echo "$_seeds_flat" | tr -d '[:space:],')" ]; then
SEEDS_JSON="["
_first=1
_oifs="$IFS"
IFS=','
for _part in $_seeds_flat; do
IFS="$_oifs"
_part=$(echo "$_part" | xargs)
IFS=','
[ -z "$_part" ] && continue
if [ "$_first" -eq 0 ]; then SEEDS_JSON="$SEEDS_JSON,"; fi
_first=0
SEEDS_JSON="$SEEDS_JSON\"$(_json_escape "$_part")\""
done
IFS="$_oifs"
SEEDS_JSON="$SEEDS_JSON]"
fi
printf '%s\n' "{" \
" \"listen\": \"0.0.0.0:$PUBLIC_PORT\"," \
" \"check_interval_minutes\": $CHECK_INTERVAL," \
" \"discovery_minutes\": $DISCOVERY_MINUTES," \
" \"stale_days\": $STALE_DAYS," \
" \"user_token\": \"$USER_TOKEN_JSON\"," \
" \"admin_token\": \"$ADMIN_TOKEN_JSON\"," \
" \"hide_addresses\": $HIDE_JSON," \
" \"seeds\": $SEEDS_JSON" \
"}" > "$STORAGE_DIR/settings.json"
fi
exec ${nodestracker_bin}/bin/nodestracker \
-data "$STORAGE_DIR" \
-listen "0.0.0.0:$PUBLIC_PORT"
'';
in
{
nodestracker = nodestracker;
}