ADR-002 — VRF Implementation: HMAC-based vs @noble/curves

Status: Accepted
Date: 2026-04-07
Accepted: 2026-05-13 (R93 post-R89B θ Phase 3)
Domain: Legitimacy (π Governance, θ Consensus, arbitration)

Context

Verifiable Random Functions (VRFs) are used in three places in Colibri’s legitimacy layer:

  1. Entropy injection (π Governance, P6.2): VRF selects 10% of voters for equal-weight ballots on proposals with >5% numeric delta.
  2. Arbiter selection (θ Consensus / arbitration): VRF selects arbiters for dispute resolution (S09, EVT-B02 ArbitersAssigned).
  3. Leader election (θ Consensus): VRF elects a BFT round leader per epoch.

A VRF must satisfy three properties:

  • Verifiable: anyone can confirm the output is correct given the proof
  • Deterministic: same input always produces the same output
  • Unpredictable: output cannot be predicted before the VRF seed is revealed

Reference algorithm: VRF specification documented in extraction files.

The reference implementation used HMAC-SHA256 as a simplified stand-in for the full ECVRF-EDWARDS25519-SHA512-TAI construction from RFC 9381.

Reference: See pseudocode in docs/reference/extractions/ for algorithm details.

Decision

Option A — HMAC-SHA256 internal implementation.

Colibri’s VRF surface uses an HMAC-SHA256 construction built on node:crypto. The implementation is not RFC 9381 ECVRF-EDWARDS25519-SHA512-TAI; it is the simplified internal-consensus VRF described in Option A below. This decision was operationalized when R89 Phase B shipped P3.6.1 (PR #239) — see src/domains/consensus/vrf-stub.ts and src/__tests__/domains/consensus/vrf-stub.test.ts. The MCP surface exposes the VRF via vrf_eval (one of the five θ tools added in R89 Phase B P3.7.1 PR #244).

The acceptance was formalized on 2026-05-13 in the R93 Phase 1 tech-debt sweep; until that round this ADR carried Status: PROPOSED even though the shipped implementation was already aligned with Option A. The rationale below (Options A/B, Consequences, Alternatives) is preserved as the original deliberation record.

Strict RFC 9381 compliance is not required at Phase 0 / Phase 1.5 / Phase 2 / Phase 3 because Colibri’s VRF use sites (governance entropy injection, arbiter selection, leader election) are internal consensus mechanisms, not externally-verifiable proofs. If a later phase needs externally-verifiable VRF proofs, a superseding ADR will migrate the surface to Option B (@noble/curves) and migrations will preserve historical proof artefacts.

Options

Option A: HMAC-SHA256 internal implementation (port the Python approach)

Implement a HMAC-SHA256 VRF using node:crypto (built-in). This is a simplified VRF approach (not full EC-VRF per RFC 9381).

Target file: src/rules/vrf.js (Phase 1+)

Pros:

  • Zero new dependencies — uses only node:crypto (built-in)
  • Sufficient for Colibri’s internal use (governance, arbitration, leader election are internal consensus mechanisms, not externally-verifiable proofs)
  • Fast: HMAC-SHA256 is a single call

Cons:

  • Not a compliant RFC 9381 ECVRF — cannot interoperate with external VRF verifiers
  • A production-grade system should use proper EC-VRF for external verifiability
  • If Colibri later exposes VRF proofs to external observers, the proofs would not be verifiable by standard tools

Option B: @noble/curves (Ed25519 EC-VRF per RFC 9381)

Use the @noble/curves library (@noble/curves/ed25519) which provides audited, pure-JavaScript Ed25519 operations. Implement the RFC 9381 ECVRF-EDWARDS25519-SHA512-TAI construction on top.

Target file: src/rules/vrf.js (Phase 1+)

Pros:

  • RFC 9381 compliant — VRF proofs verifiable by external tools
  • @noble/curves is widely used, audited, pure JS (no native addon)
  • Future-proof: if Colibri P2P layer ever exposes proofs publicly, they are standard
  • Ed25519 is consistent with Colibri identity system

Cons:

  • Adds a new npm dependency to audit and maintain
  • Implementing RFC 9381 correctly is non-trivial (~200 lines)
  • Overkill if VRF proofs are never exposed externally

Consequences

If Option A (HMAC, port Python):

  • 1-day implementation: direct port of vrf.py
  • Governance entropy injection and arbiter selection work immediately
  • Technical debt: comment in code must warn that this is not RFC 9381 compliant
  • Risk: if external verifiability is ever needed, full rewrite required

If Option B (@noble/curves):

  • 3-5 day implementation: RFC 9381 has specific hash-to-curve, nonce generation, and serialization requirements
  • VRF proofs are externally verifiable from day one
  • @noble/curves is a peer dependency of @noble/hashes (likely already in the tree via other deps)

Alternatives Considered

  • libsodium.js (wasm): provides Ed25519 but not ECVRF directly; would still need RFC 9381 layer on top
  • @peculiar/webcrypto: WebCrypto polyfill; supports Ed25519 signing but not VRF prove/verify
  • Build custom native addon with libsodium C: too heavy; native addon for a utility function is not warranted

References

  • Reference algorithm in docs/reference/extractions/
  • RFC 9381: Verifiable Random Functions (VRFs) — https://www.rfc-editor.org/rfc/rfc9381
  • @noble/curves — https://github.com/paulmillr/noble-curves
  • S09 — Arbitration — arbiter selection uses VRF
  • S18 — Governance Protocol §2.4 — entropy injection uses VRF
  • ADR-003 — BFT library decision (VRF for leader election)

Change log

  • 2026-04-07 — Drafted with Status: PROPOSED. Two options documented (Option A HMAC-SHA256 internal; Option B @noble/curves RFC 9381). Decision deferred pending PM review.
  • 2026-05-13 — Accepted (R93 Phase 1 tech-debt sweep, formalizing the Option A decision shipped in R89 Phase B P3.6.1 PR #239). Live code: src/domains/consensus/vrf-stub.ts (HMAC-SHA256 stub per Option A; NOT RFC 9381 EC-VRF). Tests: src/__tests__/domains/consensus/vrf-stub.test.ts. MCP surface: vrf_eval (one of five θ tools added in R89 Phase B P3.7.1 PR #244, surface 18 → 23). No source or test changes in this ADR-acceptance commit — the implementation already landed in R89 Phase B.

R93 Phase 1 tech-debt sweep. ADR-002 transitions PROPOSED → ACCEPTED to match the operative VRF implementation on main since R89 Phase B (2026-05-13).


Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.