The sentinel-audit CLI provides direct command-line access to the Sentinel dVPN network. It targets two audiences: developers who want scriptable chain queries without writing code, and AI agents that need structured JSON output to discover nodes, check balances, run audits, and enumerate available SDK functions.
Every command writes JSON to stdout by default. Errors go to stderr. Exit codes are strict (see Exit Codes).
Global install (recommended for regular use):
npm install -g sentinel-node-tester
sentinel-audit <subcommand> [flags]One-shot via npx (no install):
npx sentinel-node-tester <subcommand> [flags]Local development:
node bin/cli.js <subcommand> [flags]Copy .env.example to .env and set your mnemonic:
cp .env.example .envMNEMONIC=word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12
The mnemonic is the 12- or 24-word Cosmos seed phrase for the wallet that signs on-chain transactions. The derived address will have prefix sent1....
Commands that work without a mnemonic (read-only chain queries): list, nodes, node, speed, plans, functions
Commands that require a mnemonic: balance, subscriptions, test, audit, serve
For read-only commands you can also pass a wallet address explicitly via --address rather than setting a mnemonic.
| Flag | Default | Description |
|---|---|---|
--help, -h |
— | Print help for the current subcommand and exit. |
--version, -v |
— | Print package version and exit. |
--json |
on | Output raw JSON (default; machine-readable). |
--pretty |
off | Pretty-print JSON with 2-space indent. |
--lcd <url> |
auto | Override the LCD endpoint (skips automatic failover). |
--sdk <name> |
js | SDK backend to use: js or csharp. |
List all available CLI subcommands with one-line descriptions.
Syntax:
sentinel-audit list [--pretty]Flags: none beyond globals.
Example:
sentinel-audit list --prettyExample output:
{
"commands": [
{ "name": "list", "description": "List available CLI subcommands" },
{ "name": "nodes", "description": "Query active nodes from the chain" },
{ "name": "node", "description": "Query a single node by address" },
{ "name": "speed", "description": "Run a direct internet speed test (no VPN)" },
{ "name": "balance", "description": "Show wallet balance in udvpn and P2P" },
{ "name": "subscriptions", "description": "List active subscriptions for the wallet" },
{ "name": "plans", "description": "Discover active subscription plans on-chain" },
{ "name": "test", "description": "Test a single node end-to-end" },
{ "name": "audit", "description": "Run a full network audit across all nodes" },
{ "name": "serve", "description": "Start the dashboard server" },
{ "name": "functions", "description": "List all exported SDK functions" }
]
}Cost: FREE (local, no chain query)
Fetch every active dVPN node from the Sentinel chain. Results are cached in memory for 5 minutes.
Syntax:
sentinel-audit nodes [--limit <n>] [--country <code>] [--type <wireguard|v2ray>] [--pretty]Flags:
| Flag | Default | Description |
|---|---|---|
--limit <n> |
0 (all) | Return at most N nodes. |
--country <code> |
— | Filter to a two-letter ISO country code (e.g. US, DE). |
--type <name> |
— | Filter by protocol: wireguard or v2ray. |
Example:
sentinel-audit nodes --country DE --type wireguard --limit 5 --prettyExample output:
{
"count": 5,
"nodes": [
{
"address": "sentnode1abc123def456ghi789jkl012mno345pqr678stu",
"moniker": "de-node-01",
"remoteUrl": "https://185.220.101.42:8080",
"remoteAddrs": ["185.220.101.42:8080"],
"country": "Germany",
"countryCode": "DE",
"city": "Frankfurt",
"type": "wireguard",
"peers": 3,
"gigabyte_prices": [
{ "denom": "udvpn", "quote_value": "52000000" }
],
"planIds": [14, 27]
}
]
}Cost: FREE (read-only chain query)
Query a single node by its on-chain address. Includes status, location, price, and live connectivity info.
Syntax:
sentinel-audit node <sentnode1...> [--pretty]Flags: none beyond globals.
Example:
sentinel-audit node sentnode1abc123def456ghi789jkl012mno345pqr678stu --prettyExample output:
{
"address": "sentnode1abc123def456ghi789jkl012mno345pqr678stu",
"moniker": "de-node-01",
"remoteUrl": "https://185.220.101.42:8080",
"type": "wireguard",
"peers": 3,
"bandwidth": {
"download": 104857600,
"upload": 52428800
},
"location": {
"city": "Frankfurt",
"country": "Germany",
"country_code": "DE",
"latitude": 50.1109,
"longitude": 8.6821
},
"gigabyte_prices": [
{ "denom": "udvpn", "quote_value": "52000000" }
],
"clockDriftSec": 0.4,
"active": true
}Cost: FREE (read-only)
Run a direct internet speed test (no VPN tunnel). Downloads from Cloudflare CDN targets and measures throughput. Useful as a baseline before comparing tunnel speeds.
Syntax:
sentinel-audit speed [--mb <megabytes>] [--pretty]Flags:
| Flag | Default | Description |
|---|---|---|
--mb <n> |
10 | Megabytes to download during the test. |
Example:
sentinel-audit speed --mb 20 --prettyExample output:
{
"mbps": 94.7,
"chunks": 4,
"adaptive": "parallel-20mb",
"durationMs": 1688
}Cost: FREE (no chain interaction)
Show the P2P token balance for the configured wallet.
Syntax:
sentinel-audit balance [--address <sent1...>] [--pretty]Flags:
| Flag | Default | Description |
|---|---|---|
--address <sent1...> |
derived from MNEMONIC | Query this address instead of the wallet's own address. |
Example:
sentinel-audit balance --prettyExample output:
{
"address": "sent1qk2d3f5g6h7j8k9l0m1n2o3p4q5r6s7t8u9v0",
"udvpn": "4823000000",
"p2p": "4823.00"
}Cost: FREE (read-only chain query). Requires MNEMONIC in .env or --address.
List all active plan subscriptions held by the configured wallet.
Syntax:
sentinel-audit subscriptions [--address <sent1...>] [--pretty]Flags:
| Flag | Default | Description |
|---|---|---|
--address <sent1...> |
derived from MNEMONIC | Query subscriptions for this address instead. |
Example:
sentinel-audit subscriptions --prettyExample output:
{
"address": "sent1qk2d3f5g6h7j8k9l0m1n2o3p4q5r6s7t8u9v0",
"count": 2,
"subscriptions": [
{
"id": 1042,
"plan_id": 14,
"status": "active",
"expiry": "2026-07-01T00:00:00Z",
"allocated_bytes": 1073741824,
"used_bytes": 215674882
},
{
"id": 1187,
"plan_id": 27,
"status": "active",
"expiry": "2026-08-15T00:00:00Z",
"allocated_bytes": 5368709120,
"used_bytes": 0
}
]
}Cost: FREE (read-only). Requires MNEMONIC in .env or --address.
Discover active subscription plans on the Sentinel chain. Each plan bundles a set of nodes and a bandwidth price that the plan operator pre-pays on behalf of subscribers.
Syntax:
sentinel-audit plans [--limit <n>] [--pretty]Flags:
| Flag | Default | Description |
|---|---|---|
--limit <n> |
0 (all) | Return at most N plans. |
Example:
sentinel-audit plans --limit 3 --prettyExample output:
{
"count": 3,
"plans": [
{
"id": 14,
"status": "active",
"subscribers": 284,
"nodeCount": 47,
"price": [{ "denom": "udvpn", "amount": "0" }],
"provider": "sentprov1aaabbbcccdddeeefffggghhh"
},
{
"id": 27,
"status": "active",
"subscribers": 103,
"nodeCount": 12,
"price": [{ "denom": "udvpn", "amount": "1000000" }],
"provider": "sentprov1zzzyyyxxxwwwvvvuuutttss"
},
{
"id": 31,
"status": "active",
"subscribers": 58,
"nodeCount": 8,
"price": [{ "denom": "udvpn", "amount": "500000" }],
"provider": "sentprov1mmmnnnooopppqqqrrrsss"
}
]
}Cost: FREE (read-only chain query)
Test a single node end-to-end: status check, session payment, handshake, tunnel setup, speed test, and result summary. This is the same pipeline that audit runs per-node.
Syntax:
sentinel-audit test <sentnode1...> [--sdk <js|csharp>] [--mb <megabytes>] [--pretty]Flags:
| Flag | Default | Description |
|---|---|---|
--sdk <name> |
js | SDK backend: js or csharp. |
--mb <n> |
10 | Megabytes to use in speed test. |
Example:
sentinel-audit test sentnode1abc123def456ghi789jkl012mno345pqr678stu --prettyExample output:
{
"address": "sentnode1abc123def456ghi789jkl012mno345pqr678stu",
"moniker": "de-node-01",
"country": "Germany",
"countryCode": "DE",
"type": "wireguard",
"pass": true,
"actualMbps": 41.3,
"baselineAtTest": 94.7,
"ispBottleneck": false,
"pass15mbps": true,
"pass10mbps": true,
"passBaseline": true,
"googleAccessible": true,
"googleLatencyMs": 112,
"peers": 3,
"sdk": "js",
"sessionId": "8842",
"error": null,
"durationMs": 18430
}Cost: PAID — opens one on-chain session (~40 P2P for 1 GB). Requires MNEMONIC in .env.
Run a full network audit across all active nodes (or a subset). Each node is tested using the same pipeline as test. Results are written to results/results.json as tests complete.
Syntax:
sentinel-audit audit [--limit <n>] [--country <code>] [--type <wireguard|v2ray>] [--delay <ms>] [--sdk <js|csharp>] [--pretty]Flags:
| Flag | Default | Description |
|---|---|---|
--limit <n> |
0 (all) | Stop after testing N nodes. |
--country <code> |
— | Only test nodes in this country (ISO two-letter code). |
--type <name> |
— | Only test wireguard or v2ray nodes. |
--delay <ms> |
5000 | Milliseconds to wait between node tests. |
--sdk <name> |
js | SDK backend: js or csharp. |
Example:
sentinel-audit audit --country US --limit 20 --delay 7000 --prettyExample output (streaming — one object per node as it completes, then a final summary):
{ "event": "node_done", "address": "sentnode1...", "pass": true, "actualMbps": 38.1 }
{ "event": "node_done", "address": "sentnode1...", "pass": false, "error": "ETIMEDOUT" }
{ "event": "complete", "total": 20, "passed": 14, "failed": 6, "resultsFile": "results/results.json" }Cost: PAID — approximately 40 P2P per node tested (~700–800 P2P for a full audit of ~1,000 nodes). Requires MNEMONIC in .env.
Start the web dashboard server. This is the original dashboard-boot behavior: opens a browser-accessible UI at http://localhost:<port> that lets you trigger audits, browse results, and view live logs.
Syntax:
sentinel-audit serve [--port <n>]Flags:
| Flag | Default | Description |
|---|---|---|
--port <n> |
3001 (or PORT env var) |
Port to listen on. |
Example:
sentinel-audit serve --port 3005Output:
Sentinel Node Tester dashboard running at http://localhost:3005
This subcommand does not exit on its own. Send CTRL+C to stop.
Cost: FREE (no chain interaction until an audit is started from the UI)
List all functions exported from the package's programmatic API. Useful for AI agents or scripts that want to discover what is available before importing.
Syntax:
sentinel-audit functions [--pretty]Flags: none beyond globals.
Example:
sentinel-audit functions --prettyExample output:
{
"count": 62,
"functions": [
{ "name": "testNode", "module": "audit/node-test", "cost": "paid" },
{ "name": "testWithRetry", "module": "audit/retry", "cost": "paid" },
{ "name": "runAudit", "module": "audit/pipeline", "cost": "paid" },
{ "name": "runRetestSkips", "module": "audit/pipeline", "cost": "paid" },
{ "name": "getAllNodes", "module": "core/chain", "cost": "free" },
{ "name": "findWorkingLcd", "module": "core/chain", "cost": "free" },
{ "name": "getActiveLcd", "module": "core/chain", "cost": "free" },
{ "name": "queryNodeStatusDirect", "module": "core/chain", "cost": "free" },
{ "name": "invalidateNodeCache", "module": "core/chain", "cost": "free" },
{ "name": "discoverPlans", "module": "core/chain", "cost": "free" },
{ "name": "querySubscriptions", "module": "core/chain", "cost": "free" },
{ "name": "hasActiveSubscription", "module": "core/chain", "cost": "free" },
{ "name": "signAndBroadcastRetry", "module": "core/wallet", "cost": "paid" },
{ "name": "speedtestDirect", "module": "protocol/speedtest", "cost": "free" },
{ "name": "speedtestViaSocks5", "module": "protocol/speedtest", "cost": "free" },
{ "name": "nodeStatusV3", "module": "protocol/v3protocol", "cost": "free" },
{ "name": "generateWgKeyPair", "module": "protocol/v3protocol", "cost": "free" },
{ "name": "initHandshakeV3", "module": "protocol/v3protocol", "cost": "free" },
{ "name": "classifyFailure", "module": "protocol/diagnostics", "cost": "free" }
]
}The cost field is "free" for read-only operations and "paid" for functions that broadcast on-chain transactions.
Cost: FREE (local, no chain query)
An AI agent with no prior knowledge of this package can discover its full capability in three steps:
Step 1 — Enumerate subcommands:
npx sentinel-node-tester list --jsonReturns a machine-readable list of every CLI command with a one-line description.
Step 2 — Enumerate programmatic functions:
npx sentinel-node-tester functions --jsonReturns every exported function, its module path, and whether it costs tokens. The agent can then decide which functions to import for a custom script vs. which CLI commands cover the same need.
Step 3 — Pick a command and act:
# Find nodes in Japan with wireguard protocol
npx sentinel-node-tester nodes --country JP --type wireguard --json
# Check wallet balance before spending
npx sentinel-node-tester balance --json
# Test a specific node
npx sentinel-node-tester test sentnode1abc123def456ghi789jkl012mno345pqr678stu --jsonAll commands emit JSON on stdout and errors on stderr, so the agent can pipe output directly into JSON.parse() or jq.
Recommended agent flow:
list → understand what's available
nodes --limit 5 → get sample nodes to work with
balance → confirm wallet has funds before testing
test <address> → validate a single node before running audit
audit --limit 20 → run a bounded audit and inspect results/results.json
| Code | Meaning |
|---|---|
0 |
Success — command completed, JSON written to stdout. |
1 |
Runtime error — chain unreachable, mnemonic missing, node test failed. Details on stderr. |
2 |
Bad usage — unknown subcommand, missing required argument, invalid flag value. |