Skip to main content
This walks through joining Pinaivu’s live testnet as a node operator: your machine runs the inference (via Ollama), connects to the coordinator’s libp2p mesh, bids on requests, and serves real traffic end-to-end through the production infra — auction, dispatch, settlement, the whole path. This is different from the smoke test: that guide verifies one specific settlement as an internal check. This guide is for anyone who wants to contribute compute and watch their own node pick up and answer real requests.

Prerequisites

PieceRequirement
Rust toolchaincargo build --release for the node
Ollamarunning locally with at least one model pulled
A Sui testnet addressrecommended — this is where your testnet settlement payouts land
The current testnet coordinator:
export COORDINATOR_HOST="13.206.80.190:4000"
This address will change if the coordinator redeploys to new infrastructure — if it stops responding, check back here for an updated value, or confirm liveness with the health check below before reporting an issue.

Step 1 — confirm the coordinator is live

curl -sk https://$COORDINATOR_HOST/health
# ok

curl -sk https://$COORDINATOR_HOST/enclave_health | jq .
A real response looks like this:
{
  "public_key_hex": "f7f87f415ef041c7ec7593604db48d8b71840401847777862a9941162e876140",
  "x25519_pubkey_hex": "b2fd6a7cb94f3671bbb39a889b7ed80d15c4ad6a4ec0fb79b22a1f54b41e6d34",
  "peer_id": "12D3KooWSWLeCmHNiwRRkQspzFbnSCD9pMaDdT17nydK4qGoKiNj",
  "uptime_ms": 1615967,
  "enclave_object_id": "0x50d817ebf12e9106531a3faffea5f13a78b04408a0a3d11df6380e8d331d6547",
  "sui_tx_digest": "2uyrEALbcwpjW163pJyg3kZycSiQKQdiiT3jFeXhwkjj",
  "tls_cert_fingerprint": "eb245421971796caedf22a8b8555fe979182c0ea1f6e68061a3c42a4cdf83df9"
}
A non-null enclave_object_id means this coordinator instance has already registered its attested key on Sui — you can look that object up on-chain and compare its PCRs against what /get_attestation reports below.
curl -sk https://$COORDINATOR_HOST/get_attestation | jq .
{
  "pcr0": "d1b17f0e4be79d37439e06b804ac35a7a0a4f7c28b29584b572bcaac79860073ce38ca3cf42a00c48e2cbddbf89541a2",
  "pcr1": "d1b17f0e4be79d37439e06b804ac35a7a0a4f7c28b29584b572bcaac79860073ce38ca3cf42a00c48e2cbddbf89541a2",
  "pcr2": "21b9efbc184807662e966d34f390821309eeac6802309798826296bf3e8bec7c10edb30948c90ba67310f7b964fc500a",
  "public_key": "f7f87f415ef041c7ec7593604db48d8b71840401847777862a9941162e876140",
  "timestamp_ms": 1782074702677,
  "cbor_len": 9008
}

Step 2 — get the coordinator’s current peer_id

export PEER=$(curl -sk https://$COORDINATOR_HOST/enclave_health | jq -r .peer_id)
echo $PEER
The coordinator’s enclave generates a fresh keypair on every deploy, so the peer_id rotates each time it redeploys. Always fetch it live rather than hardcoding a value you saw once — a stale peer_id won’t dial through.

Step 3 — build the node

git clone https://github.com/Pinaivu-AI/Node.git
cd Node
cargo build --release
If you already have a checkout, rebuild before connecting — the node shares its wire protocol with the coordinator, and an out-of-date binary will fail to dial with a protocol negotiation error.

Step 4 — run your node against the testnet coordinator

COORDINATOR_TCP="${COORDINATOR_HOST%:*}"

INSECURE_COORDINATOR=1 \
./target/release/pinaivu-node \
  --coordinator-addr  /ip4/$COORDINATOR_TCP/tcp/4001/p2p/$PEER \
  --coordinator-http  https://$COORDINATOR_HOST \
  --listen            127.0.0.1:5000 \
  --advertise-url     http://127.0.0.1:5000 \
  --ollama-url        http://localhost:11434 \
  --model             llama3.2:1b \
  --payout-address    <your Sui testnet address>
Pass --payout-address with a Sui testnet address you control — this is how the network pays you for the requests your node serves. Without it, your node still bids and serves requests, but there’s nowhere for settlement to send your share. INSECURE_COORDINATOR=1 tells the node to accept the coordinator’s self-signed TLS certificate (the cert is bound to the enclave’s attested key, not a public CA — that’s expected for testnet). Watch the logs for connection established peer=<coordinator_peer_id> to confirm the mesh handshake succeeded.

Step 5 — confirm requests are landing on your node

Send a test request through the coordinator the same way any client would. Inference is dispatched to the winning node over libp2p directly — the coordinator waits for the node’s signed completion and returns the final answer in one response, so there’s no separate request to your node’s HTTP port:
curl -sk -m 20 -X POST https://$COORDINATOR_HOST/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{
    "model": "llama3.2:1b",
    "messages": [{"role":"user","content":"say hi"}],
    "client_pubkey_hex": "'$(printf '01%.0s' {1..32})'"
  }' | jq .
{
  "request_id": "f4fa06c3-96d8-42a4-80f2-6167d96a0fa3",
  "session_id": "cde15f37-077b-4a1e-8080-7dad8e942c3e",
  "content": "Hello, how are you?",
  "session_key": "xKRmG0hU3JM2VSUq9koq7AL8NW45WeD5blOP4fpXA0I=",
  "input_tokens": 45,
  "output_tokens": 7,
  "latency_ms": 310
}
If your node won the auction, your node’s own logs will show the full round trip:
received inference request request_id=... model=llama3.2:1b
published bid request_id=...
received inference dispatch over libp2p request_id=...
sent CompletionAck request_id=...
coordinator accepted CompletionAck req=Some(...)
If you don’t win every time, that’s expected — other nodes are bidding on the same mesh, and the auction picks based on price, latency, and reputation.

Running multiple local LLMs

Nothing stops you from joining the mesh with more than one backend at once. Each node instance is just a separate process with its own listen port and its own model — run as many as you have local capacity for:
# Node A — backed by llama3.2:1b
INSECURE_COORDINATOR=1 ./target/release/pinaivu-node \
  --coordinator-addr  /ip4/$COORDINATOR_TCP/tcp/4001/p2p/$PEER \
  --coordinator-http  https://$COORDINATOR_HOST \
  --listen 127.0.0.1:5000 --advertise-url http://127.0.0.1:5000 \
  --ollama-url http://localhost:11434 --model llama3.2:1b &

# Node B — backed by a different model (or a different Ollama instance entirely)
INSECURE_COORDINATOR=1 ./target/release/pinaivu-node \
  --coordinator-addr  /ip4/$COORDINATOR_TCP/tcp/4001/p2p/$PEER \
  --coordinator-http  https://$COORDINATOR_HOST \
  --listen 127.0.0.1:5001 --advertise-url http://127.0.0.1:5001 \
  --ollama-url http://localhost:11434 --model mistral &
Each instance needs its own --identity-file (defaults to ~/.pinaivu-node/identity.key, so give each one a distinct path) and bids independently. The coordinator’s auction doesn’t know or care that both are running on the same host — from its side they’re just two more peers in the mesh, each capable of winning different requests based on price, latency, and reputation. This is the simplest way to add capacity to the network without provisioning a second machine.