Skip to content

Nuvex is tracked in milestones, and the repository does not record a date for any of them. Each entry below is taken from the protocol README and the status line of the decision records it amended, so a row says what was implemented rather than when.

Milestone 0

Complete

Workspace initialisation

The repository, the toolchain and the specifications, with no instruction handlers behind them.

  • ADR 0001 accepted: the protocol is job-agnostic, and three programs own the on-chain surface.
  • ADR 0002 accepted as a layout specification. Every account is a PDA and no account stores a URL or an unbounded list.
  • Toolchain pinned: Anchor 1.2.0, Solana CLI 4.1.2, Solana crates 3.x, Rust 1.89 MSRV, Node.js 22, TypeScript 5.9.3, pnpm 10.15.1.
  • Security documents written: audit scope, threat model, key management, incident response and upgrade policy.
  • No instruction handlers, no token mint, and none of the four key roles loaded.

Written down in: ADR 0001, ADR 0002, security/AUDIT_SCOPE.md, security/KEY_MANAGEMENT.md

Milestone 1

Complete

Accounts and the transition graph

The request and registry accounts exist, and the legal state changes are classified.

  • ADR 0001 amended with the transition graph. transition in nuvex-protocol-types classifies every pair of states.
  • Implemented edges: Pending to Cancelled by the requester while slot is below expires_slot, and Pending to Expired by any crank once slot has reached it.
  • ADR 0002 amended for the request and registry accounts. ProtocolConfig, NodeRegistry and Node are initialised once by the deployer.
  • A request copies expires_slot at creation, so a later job-config edit does not rewrite it.
  • Instructions that exist check their PDA seeds.

Written down in: ADR 0001, ADR 0002, security/THREAT_MODEL.md

Milestone 2

Complete

VRF fulfilment, verification and the empty-account callback

A proof is checked inside a program, and the output reaches a callback that holds no accounts.

  • ADR 0004 accepted for VRF: solana-ecvrf 0.0.1, ECVRF-EDWARDS25519-SHA512-TAI from RFC 9381, suite byte 0x03, 80-byte proof, 64-byte output. No audit report of that crate was found.
  • Alpha is built by nuvex-vrf and never taken from instruction data, so a user cannot submit an unbound alpha.
  • VrfResult is created by oracle core through CPI, with the protocol PDA as the required signer.
  • ADR 0006 implemented for the empty-account callback: the CPI passes zero accounts, does not include the request, and a failing callback reverts the transaction.
  • A second fulfilment is rejected, and no reward is paid because no fee moves.

Written down in: ADR 0001, ADR 0004, ADR 0006, Protocol README

Milestone 3

Complete

Stake configuration, the eligibility predicate and the assignment snapshot

Fulfilment now requires stake, an active status and a fresh heartbeat, and the request keeps the values that were checked.

  • ADR 0003 amended: the selection function is the eligibility predicate, and it is not a weighted lottery.
  • A fulfiller must be Active, hold stake at or above min_stake, have a heartbeat inside the configured window, and have registered its VRF key before the request.
  • fulfill writes assigned_node, assigned_stake and assigned_heartbeat, so an observer sees the values that were checked rather than a later balance.
  • Node statuses are Registered, Active and Unstaking. The unstake cooldown and the heartbeat window are registry fields bounded to 1..=150_000.
  • Still open at this milestone: max_fee is stored and never charged, the node process does not submit transactions, and an operator who funds several keys can choose among those outputs.

Written down in: ADR 0002, ADR 0003, Protocol README status

Milestone 4

Complete

Indexer, PostgreSQL, read API and dashboard reads

An indexer decodes known accounts and writes a read model. The API and console can show those rows. None of that is protocol truth.

  • The indexer polls getProgramAccounts, decodes request, node, registry, protocol and VrfResult layouts, and upserts PostgreSQL or a JSON snapshot.
  • GET /v1/requests, /v1/nodes and /v1/network return 200 from that store, including an empty list. They return 503 when no store is configured. Jobs, models and prices stay 501.
  • The API still reports authority: none. A missing indexed row is not proof the account is absent on chain.
  • The console reads NEXT_PUBLIC_API_URL when set. Illustration panels stay labelled. The CLI network command reads NUVEX_API_URL and refuses to invent status if it is unset.
  • Fees stay unset until ADR 0005 names basis points, and there is no mainnet deployment.

Written down in: Protocol README, nuvex-services README, ADR 0005

Milestone 5

Current

Public price adapters, freshness, and a median

The API can median fresh public observations. That number is not written on-chain, and a short set returns no price.

  • USD observations come from Coinbase, Kraken, and Pyth. USDT observations come from Binance and Bybit. The quotes are not mixed.
  • An observation counts only when the provider timestamp is inside the requested window. The default window is 60 seconds and the default minimum is 2 sources.
  • GET /v1/prices returns 200 with authority: none when enough sources are fresh, and 422 with a null median otherwise. Pyth's unauthenticated Hermes response is recorded as a rejection.
  • Programs still reject Price and Data requests. The node process does not fetch venues or submit a price transaction. Data jobs stay unspecified.
  • Fees stay unset until ADR 0005 names basis points, and there is no mainnet deployment.

Written down in: ADR 0004, nuvex-services data/, Protocol README