Skip to main content

// Chapter 14 · Operate

Node CLI

The network is CLI-first for one reason: operators run their nodes on headless machines, often through SSH, often as a service.

10 min9 sectionsOperate

CLI-first because operators run on headless machines.

// 14.0 · v1.2.1 · runs on ssh + as a service · ollama-backed

// Daemon shape

Node key, probe, init, start, service, heartbeat, serve, claim-elsewhere. Eight responsibilities.

The Node CLI is CLI-first because operators run their nodes on headless machines, often through SSH, often as a managed service. It runs on macOS and Linux; on Windows, inside WSL2. Inference executes on Ollama, the open-source LLM runtime the one-line installer sets up for you.

Staking, claiming rewards, and unstaking are on-chain wallet actions done in the Operator Console (registerNode, claim, requestUnstake, withdrawStake), not CLI subcommands. The CLI is the off-chain half: it attaches a machine to a node the wallet already staked and keeps it online with liveness heartbeats. It uses a separate, low-value node keypair, never the staking wallet's key. The two halves share only the nodeId.

// Console · Operate · nodesOpen in console
// operator · activity feed live · sse
  • 00:00registerednode #4231 · stake 50,000 $PRLX
  • 00:04onlineCLI attached · heartbeating · state active
  • 01:12request servedinference · served whole · PoE ✓
  • 01:12reward accrued+12.84 $PRLX · uptime credit

Nodes serve whole ParalleliX AI inference requests. There is no task submission or sub-task fan-out on the live path.

The Console shows each node's online status and live earnings. The CLI is the off-chain half that keeps the machine online.

// Console · Operate · earningsOpen in console
// operator · earningsaccrues every block
claimable
23,515.31$PRLX
// daily share
weight = stake x tier x uptime
share = daily_pool x weight / Σ weight
source: 25% Operator Rewards bucket · ~34,200 $PRLX/day via Sablier
claim(nodeId)wallet signs · paid to owner

Per-day reward breakdown sourced from the 25% Operator Rewards bucket, which unlocks linearly via Sablier, or a share of ParalleliX AI usage in steady state. Rewards accrue on-chain and are claimed in the Console.

What the CLI does

// 14.1 · eight responsibilities · the daemon's hot path is poll · run · commit · sign · return

// Daemon responsibility register · 8 entries

  • // 14.1.1Node key management

    Generate and protect a node secp256k1 key: a separate, low-value liveness key, never the staking wallet's key.

  • // 14.1.2Attach by nodeId

    Attach the machine to a node already registered on-chain in NodeRegistryLocker, then submit the signed capability manifest.

  • // 14.1.3Liveness heartbeats

    Send signed liveness heartbeats to the coordinator every 10s. An online node is what OperatorStakeRewardsV2 streams rewards to; offline nodes do not accrue.

  • // 14.1.4Coordinator link

    Poll the coordinator for dispatched requests and maintain liveness; heartbeat every 10s.

  • // 14.1.5Inference execution

    Run each dispatched inference request whole through Ollama on local hardware. A single inference is served by one node, not split.

  • // 14.1.6Proof-of-execution

    Compute the SHA-256 PoE and return personal-signed results.

  • // 14.1.7Run as a service

    Install as systemd (Linux) or launchd (macOS) to run 24/7, survive reboot, and auto-restart.

  • // 14.1.8Local status + logs

    status prints node state; logs tails ~/.parallelix/node.log.

Command surface

// 14.3 · 9 subcommands · all accept --json

// parallelix-node · subcommands9 commands

  • parallelix-node probe

    Detect local hardware + Ollama; print the tier it maps to

  • parallelix-node init

    Generate the secp256k1 node key (a separate, low-value key) and config; print the nodeKeyHash

  • parallelix-node models [pull <name>]

    List local Ollama models, or pull one (defaults to llama3.2)

  • parallelix-node start

    Run the daemon: attach to the on-chain nodeId, heartbeat, serve inference, return PoE (--gpu|--cpu, --model)

  • parallelix-node service

    Install as a background service (systemd/launchd): 24/7, survives reboot, auto-restarts

  • parallelix-node verify

    Run diagnostics: Node version, key permissions, Ollama + model, coordinator reachability

  • parallelix-node status

    Print local node state (address, nodeKeyHash, tier, contracts)

  • parallelix-node logs

    Tail the node log (~/.parallelix/node.log)

  • parallelix-node version

    Print the CLI version

State machine

// 14.4 · start binds to an on-chain nodeId · unstake is initiated in the Console · transitions emit state.transition

// daemon · state machine
        ┌──────────┐
        │  FRESH   │  (installed, no node key)
        └────┼─────┘
             │ init  (generate node key, print nodeKeyHash)
             ▼
        ┌──────────┐
        │  INITED  │  (node key present, config written)
        └────┼─────┘
             │ registerNode in Console  →  nodeId
             │ start --node-id N
             ▼
        ┌──────────┐
        │  ACTIVE  │  (online, heartbeating, serving; accruing rewards)
        └────┼─────┘
             │
   ┌─────────┼─────────────┐
   ▼         ▼             │ requestUnstake() in Console  →  coordinator 403
 (idle)   (running          ▼
 machine   inference)    ┌──────────┐
 off)         │          │ COOLING  │  (daemon self-stops; 7-day cooldown)
   └────┼─────┘          └────┼─────┘
        ▼                     │ withdrawStake() in Console after cooldown
        loop                  ▼
                         ┌──────────┐
                         │ RETIRED  │  (principal returned; nodeId retired)
                         └──────────┘

The hot path

// 14.6 · poll, run, commit, sign, return · run is the only slow step

// Per-request hot path · 6 stepsexecute = slow step

  1. // 01

    Poll

    GET coordinator /operator/inbox?nodeId=<id> for a dispatched request. One whole inference request.

  2. // 02

    Run

    Ollama /api/generate with the model and prompt, whole on the local GPU/CPU. The only slow step.

  3. // 03

    Commit

    poe = sha256(request_id ‖ result ‖ node_id).

  4. // 04

    Sign

    signature = personal-sign(node.key, "parallelix-node:result:<id>").

  5. // 05

    Return

    POST coordinator /operator/result { result, poe, signature }.

  6. // 06

    Heartbeat (in parallel)

    Every 10s, personal-sign and POST /operator/heartbeat. This keeps the node online and accruing.

Status and logs

// 14.9 · status prints node state · logs tails ~/.parallelix/node.log

// status + logs
$ parallelix-node status
// node status
· node address   0x…
· nodeKeyHash    0x…
· tier           3
· coordinator    https://parallelix.io/api
· ollama         http://127.0.0.1:11434

$ parallelix-node logs
2026-… start node=4217 mode=gpu model=llama3.2
2026-… served req_a3f1 model=llama3.2 ms=47 poe=0x…
2026-… served req_a3f5 model=llama3.2 ms=92 poe=0x…

Security primitives

// 14.10 · key ownership · signature on every message · no outbound credentials

// Security invariants · 4 entries

  • // 01

    Separate node key. A secp256k1 key generated on first init, stored at ~/.parallelix/node.key with mode 0600. It signs liveness and results only, never the staking wallet's key. init --force overwrites it and orphans the old node.

  • // 02

    Signature on every message. Every heartbeat and result is Ethereum personal-signed by the node key. The coordinator verifies it; unsigned or mis-signed messages are ignored.

  • // 03

    Self-stop on unstake. The coordinator returns 403 not_staked or in_cooldown for a node no longer backed on-chain; the daemon prints the reason and exits cleanly rather than spinning.

  • // 04

    No outbound credentials. The CLI emits no wallet private keys, no contract-owner keys, no third-party tokens.

// Where to go next · reading path