Skip to content

Milestone 3 · Security

Nuvex maintainers
A circuit layout photographed as white traces on black

The admission, in the project's own words

The status paragraph of the protocol README carries a limitation rather than hiding it in an issue tracker. It is the first thing a reader of this post should see, unedited.

“An operator who funds several keys can still choose among those outputs. The cost of each extra key is the configured minimum stake. A minimum of zero does not resist that.”
Protocol README, Status

A node registers a VRF public key, and one key produces exactly one output for one alpha. Nothing stops the same operator from registering a second key, a third and a fourth before a request exists, proving the output under each of them, and submitting whichever output it prefers. Every one of those proofs verifies, because every one of them is a correct proof under a registered key.

Why the program cannot simply draw a winner

The obvious fix is to pick the fulfiller rather than let it pick itself. ADR 0003 explains why that instruction does not exist yet: there is no on-chain list of nodes, so a program has nothing to draw from, and a requester-chosen seed could be ground until a preferred key wins.

“There is no on-chain list of nodes. A program cannot draw a winner from every registered key. A seed chosen by the requester could be ground until a preferred key wins. This milestone does not add that draw.”
ADR 0003: Node selection

A weighted draw is also not a neutral piece of engineering. Weighting stake linearly favours capital and weighting reputation favours incumbents, so the ADR records the choice as an economic policy that has not been made. Reputation stays a plain counter and is not a weight.

Eligible nodesstake · heartbeat
  • Node 01heartbeat in windowFulfilled
  • Node 02heartbeat in windowEligible
  • Node 03heartbeat in windowEligible
  • Node 04heartbeat staleIneligible
Illustration · not chain data
Illustration · not chain data

The price of another identity

The only cost of an extra key is the deposit that key has to lock. The registry authority sets that minimum, and zero is an allowed value, which means the configuration can switch the defence off entirely. The threat model says so in the row for a Sybil cluster.

“Each identity must lock min_stake. A minimum of zero does not stop the choice. See ADR 0003.”
security/THREAT_MODEL.md, Sybil cluster
Stake and heartbeatper node

Minimum stake

configured

Heartbeat window

configured

Registered before request

required

Slash

available

Illustration · not chain data

What still holds

The weakness is a bias on which eligible output reaches the chain first. It is not a licence to invent an output, and the checks around it are implemented and tested.

  • A proof is bound to one request: the alpha is built from the request account, the job type and the input, and is never read from instruction data.
  • A node account must have been created at a slot strictly earlier than the request, so a key cannot be added after seeing one.
  • The VRF public key is set at registration and update_node cannot replace it.
  • The capability mask must include the VRF bit.
  • A forged proof is rejected by the verifier, and the tests cover a late key, a missing capability bit and a forged proof.

The verification ADR draws the line in one sentence: uniqueness is something a consumer may rely on, and unbiasedness against a multi-key operator is not.

“A consumer cannot rely on the output being unbiased against an operator who registered many keys before the request.”
ADR 0004: Verification model

What would close it

Closing the gap needs an on-chain index of nodes to draw from, a weight that someone has to justify, and a seed that a requester cannot grind. None of those three exist in this tree, so the honest position is to publish the limitation next to the capability instead of describing the randomness as unbiased.

Nothing is deployed to mainnet while that stands: scripts/deploy-mainnet.sh exits before any transaction, and no independent audit has been commissioned. Milestone 4 copies the assigned node, stake and heartbeat from each request into a read model so they can be inspected without a cluster subscription. The account is still the result.

A night sky over a mountain ridge
Nuvex maintainersMilestone 5

Protocol

A price median is not a proof

Milestone 5 asks public venues for SOL, BTC, and ETH, drops anything stale or failed, and returns the median only when enough sources remain. The programs still reject a Price request.

Read the post
Earth from orbit beside a spacecraft
Nuvex maintainersMilestone 4

Protocol

The read model copies accounts. It does not decide them

Milestone 4 adds an indexer, a PostgreSQL store, and a read API the console and CLI can query. An empty list is a real answer. A row in that list is still not a finalized result.

Read the post