Skip to main content
The pinaivu-protocol crate (coordinator/src/pinaivu-protocol/) defines every type that goes over the wire — both gossipsub broadcasts and request-response messages — plus signing helpers. Both the coordinator and the node depend on it; the node’s git dependency points at Pinaivu-AI/Coordinator with package = "pinaivu-protocol" so providers don’t need to clone the coordinator code itself.

Types

TypeWhere it travelsSigned by
InferenceRequestgossipsub /pinaivu/inference/any/1.0.0none (client identity carried in body)
InferenceBidgossipsub /pinaivu/bids/1.0.0none in v1 (libp2p mesh authenticates the sender)
DispatchTokenHTTP body (coordinator → client → node)coordinator’s enclave key
ProofOfInferencenested inside CompletionAckeach contributing node’s key
CompletionAcklibp2p request-response /pinaivu/completion/1.0.0primary node’s key (covers the proof set)
RecruitRequest / RecruitResponselibp2p request-response /pinaivu/recruit/1.0.0primary’s signed request; helper returns a signed proof
RoutingReceiptstored in Postgres, served via GET /v1/proofs/{id}coordinator’s enclave key

Signing format

All signatures are Ed25519. Most types use serde-JSON canonical bytes as the signed message (canonical_bytes() on each type). RoutingReceipt is different — it’s signed as a BCS-encoded IntentMessage, matching the shape pinaivu::receipts::ReceiptPayload expects on-chain so enclave::verify_signature accepts it natively:
sign(  bcs(IntentMessage {
         intent:        1,             // INTENT_ROUTING_RECEIPT
         timestamp_ms:  u64,
         payload:       ReceiptPayload {
            request_id:             vector<u8>,     // 16-byte UUID
            aggregated_output_hash: vector<u8>,     // 32-byte SHA-256
            payouts:                vector<Payout>, // {sui_address: address, amount: u64}
         },
       })
    )
v1 limitation: the receipt signature only covers the settlement subset(request_id, aggregated_output_hash, payouts). The receipt struct also carries descriptive metadata (client_id, primary_peer_id, helper_peer_ids, proof_ids, bid_set_hash) that the off-chain explorer renders, but those fields are not cryptographically committed in v1. A future tighter signature can add a full_receipt_hash covered by the same payload.

Topic names

gossipsub:
  /pinaivu/inference/any/1.0.0   broadcast inference requests
  /pinaivu/bids/1.0.0            broadcast bids
  /pinaivu/announce/1.0.0        (planned) periodic NodeCapabilities
  /pinaivu/reputation/1.0.0      (planned) reputation roots

request-response:
  /pinaivu/completion/1.0.0      node → coordinator: CompletionAck
  /pinaivu/recruit/1.0.0         node → node:        RecruitRequest

kademlia:
  /pinaivu/kad/1.0.0             DHT (isolated from the public libp2p DHT)

libp2p behaviour composition

pinaivu_protocol::mesh::PinaivuBehaviour is a single #[derive(NetworkBehaviour)] struct used by both the coordinator and every node, so peer-id derivation, gossipsub mesh formation, and request-response routing are identical on both sides:
PinaivuBehaviour {
    gossipsub:  pub/sub for auction topics over the decentralized GPU mesh
    kademlia:   peer routing
    identify:   protocol negotiation + observed addrs
    ping:       liveness
    completion: request_response::cbor for CompletionAck
    recruit:    request_response::cbor for RecruitRequest
}

Multi-node recruitment (/pinaivu/recruit/1.0.0)

The wire-level primitives for a primary node to recruit a helper node for part of a job are shipped — RecruitRequest/RecruitResponse round-trip correctly, and CompletionAck.proofs already accepts more than one ProofOfInference. The coordinator extracts proofs[0] as primary and proofs[1..] as helpers when it builds the RoutingReceipt, with no coordinator-side changes needed. The actual orchestration — deciding how to split a job, which helper to pick, and running the map/reduce — is intentionally out of scope for the node binary itself and is handled by a separate orchestration engine that drives the SendRecruit command.