PRAHARI प्रहरी
Post-quantum Random Accumulator — Hardware-Assured Resilient Integrity
SHA3-256 sponge engine absorbing 5 hardware entropy sources for post-quantum key generation.
v3.3.0The Sentinel at the Mountain Pass
A prahari stands watch at the mountain pass — vigilant, tireless, guarding what flows through. Nepal’s border sentinels carry this title. In yakmesh, PRAHARI guards the entropy pipeline: a SHA3-256 sponge that continuously absorbs hardware entropy from five independent sources, squeezing cryptographic seeds on demand. Every ML-KEM-768 key in the mesh traces its randomness back to this watchman’s vigil.
v3 — From Slots to Sponge
PRAHARI v3 replaces the v2 slot-array design (inherited from STEADYWATCH) with a clean SHA3-256 sponge. Gone are the 144-slot seed array, SST family grouping, and Fibonacci-cycle slot selection. The sponge design is simpler, smaller (~128 bytes state vs 4,608), and continuously refreshed from hardware — not a static constellation of pre-harvested seeds. The SST mathematics that powered those systems remain central to Yakmesh via TRIBHUJ, YPC-27, and 162T routing — PRAHARI simply no longer uses slot-based selection from SST families.
Overview
PRAHARI manages a SHA3-256 sponge that continuously absorbs entropy from multiple hardware sources.
When keys are needed, getHybridSeed() squeezes 64 bytes from the
sponge and XORs them with crypto.randomBytes(64) — a two-source
extractor that guarantees cryptographic strength even if one source is compromised.
The system includes an Entropy Sentinel for quality scoring (NPU-accelerated via TIVRA) and TRIBHUJ ternary quality verdicts for mesh-consensus entropy assessment.
Two-Source Extractor
The security model is a two-source extractor. Two independent entropy pools are XORed, so an attacker must compromise both simultaneously:
Sponge (Hardware Sources)
SHA3-256 accumulator absorbing RDRAND, GPS jitter, interrupt timing, mesh packet arrival, and CSPRNG. Continuously refreshed every 10 seconds.
OS CSPRNG
crypto.randomBytes(64) — independent of the
sponge. Even if sponge state is fully known, the hybrid seed remains at least as strong as the OS
entropy.
import { getHybridSeed } from 'yakmesh/security/prahari';
// Returns 64-byte Uint8Array for ML-KEM-768 keygen
const seed = getHybridSeed();
// Both sources must fail simultaneously for compromise.
// Sponge compromised → CSPRNG still protects.
// CSPRNG compromised → sponge (5 hardware sources) still protects.
Five Entropy Sources
The sponge absorbs from five independent hardware entropy sources. Sources register dynamically
as subsystems come online via the pluggable EntropySourceRegistry.
| Source | Kind | Weight | Description |
|---|---|---|---|
| RDRAND/RDSEED | rdrand |
8 | CPU hardware RNG (via VAES/AVX-512 extensions) |
| GPS Jitter | gps-jitter |
5 | Hardware GPS timing residuals via MANI |
| Interrupt Timing | interrupt |
3 | process.hrtime.bigint() nanosecond deltas — CPU
TLB, cache, branch prediction jitter |
| Mesh Packets | mesh-arrival |
4 | WebSocket message interarrival jitter from the P2P mesh |
| OS CSPRNG | csprng |
10 | crypto.randomBytes — always available, always
strong |
import { registerEntropySource } from 'yakmesh/security/prahari';
// Register a custom entropy source (e.g., external TRNG)
registerEntropySource({
kind: 'external-trng',
name: 'USB Hardware RNG',
weight: 7,
available: () => trngDevice.isConnected(),
harvest: () => trngDevice.read(32),
});
PrahariSpongeEngine
The core PrahariSpongeEngine is a SHA3-256 sponge accumulator.
It absorbs entropy from all registered sources and squeezes domain-separated output on demand.
Sponge Operations
SHA3-256(state || domain || newEntropy) → new 64-byte state.Domain-separated with
PRAHARI-ABSORB label. Counter-extended to maintain 64-byte state width.
SHA3-256(state || label-i)
concatenated to desired length.Domain-separated per output chunk. Never reveals the actual sponge state.
PRAHARI-RESEED domain label.
import { initialize, getHybridSeed, getStatus } from 'yakmesh/security/prahari';
// Initialize the sponge engine
await initialize({
persistPath: './data/prahari-state.bin', // optional persistence
inferenceEngine: accelInference, // optional: NPU for Sentinel
});
// Primary integration point (called by ANNEX before ML-KEM-768 keygen)
const seed = getHybridSeed(); // Uint8Array(64)
// Full status report
const status = getStatus();
// {
// initialized: true,
// sponge: { absorbCount: 847, state: '(hidden)', sizeBytes: 64 },
// sources: { rdrand: { available: true, contributions: 312 }, ... },
// sentinel: { available: true, method: 'npu' },
// telemetry: { hybridSeeds: 42, sentinelPasses: 40, ... }
// }
Entropy Sentinel
The EntropySentinel scores the quality of entropy material.
When an ONNX model is loaded and an NPU/GPU is available
(via TIVRA), it uses
hardware-accelerated pattern detection. Otherwise, it runs four software statistical tests:
| Test | Weight | Detects |
|---|---|---|
| Bit-entropy (per byte) | 40% | Low information density |
| Chi-square uniformity | 25% | Byte frequency bias |
| Run-length test | 15% | Repeating byte sequences |
| Autocorrelation (lag 1) | 20% | Sequential patterns |
The combined score maps to a TRIBHUJ ternary verdict:
import { scoreEntropy } from 'yakmesh/security/prahari';
const result = await scoreEntropy(seedBytes);
// {
// score: 0.94,
// verdict: Trit(POSITIVE),
// method: 'npu', // or 'gpu' or 'cpu'
// details: { bitEntropyPerByte: 7.82, chiSquare: 241.3, ... }
// }
SST & Ternary Mathematics
While v3 no longer uses SST families for slot selection, the SST mathematics behind Yakmesh’s ternary stack remain fundamental. PRAHARI’s ternary quality verdicts flow directly from TRIBHUJ balanced ternary primitives:
TRIBHUJ
Balanced ternary algebra. Trit/TritArray primitives used by PRAHARI for quality verdicts (POSITIVE, NEUTRAL, NEGATIVE).
YPC-27
Polynomial ring Z[x]/(x27−1) mod 3. SIS-hard checksums used by trit-commitment for 162T integrity.
162T Routing
3×54-trit hierarchical addresses replacing 144T. 256.8-bit PQ security with zero YPC-27 padding waste. PRAHARI seeds feed into ML-KEM-768 keys that protect these addresses.
API Reference
initialize(options) → Promise<{ initialized, sentinel }>
Start the sponge engine, register built-in entropy sources, load persisted state, begin reseed timer.
getHybridSeed() → Uint8Array(64)
Squeeze 64 bytes ⊕ CSPRNG. Primary ANNEX integration point for ML-KEM-768 keygen.
scoreEntropy(data) → Promise<{ score, verdict, method, details }>
Score entropy quality of arbitrary byte data. NPU/GPU/CPU tiered inference.
registerEntropySource(source) → boolean
Register a custom entropy source into the pluggable registry.
getStatus() → Object
Full status: sponge state, source contributions, sentinel state, telemetry counters.
seedStore.initialized → boolean
Backward-compatible flag checked by ANNEX before keygen.
Public Entropy Beacon API
Every YAKMESH node exposes its mesh-consensus entropy as a paranoid entropy beacon.
Pulses are signed with the node's ML-DSA-65 identity key, chained via
previous_signature, and timestamped by AGUWA.
Open CORS and 300 req/min rate limits make this suitable for external entropy consumers.
Pulse Format
Every pulse contains these fields:
| Field | Type | Description |
|---|---|---|
| round | number | Commit-reveal round number |
| randomness | string (hex) | 64 bytes (128 hex chars) of consensus entropy |
| timestamp | number (unix) | AGUWA-synchronized time (not wall clock) |
| previous_signature | string | null | Signature of previous pulse (genesis = null) |
| signature | string (hex) | ML-DSA-65 signature over round+randomness+timestamp+previous_signature |
| public_key | string (hex) | Node's ML-DSA-65 public key for verification |
| node_id | string | ID of the beacon node |
GET /info → JSON
Root-of-trust info: public_key, period
(seconds),
threshold (min contributors), next_expected, total_pulses.
Call this once to establish trust before consuming pulses.
GET /public/latest → Pulse
Most recent signed pulse. Returns 503 if no pulse available yet (node still booting or no peers).
GET /public/{round} → Pulse
Pulse for a specific round. Returns 404 if round not in history (pruned or not yet reached).
GET /public?limit=100&offset=0 → { pulses, limit, offset }
Paginated pulse history, newest first. Max limit=1000. Nodes
retain up to 1,000 pulses (bounded history).
POST /public/verify → { valid,
verifiedAgainst }
Server-side signature verification. Accepts a pulse object in the body.
Optional public_key parameter for cross-node verification.
Client Verification Checklist
- Signature: Verify
signatureagainst the pulse fields using the node'spublic_key. The signed payload isJSON.stringify({ round, randomness, timestamp, previous_signature }). - Chain link: Confirm
previous_signaturematches thesignatureof the preceding round. Genesis pulse hasnull. - Freshness: Check
time.now() - timestamp ≤ 120s(or2 × period). Note: timestamps are AGUWA-synchronized, not wall clock — allow for ±2s drift. - Randomness length: Ensure
randomness.length === 128hex chars (64 bytes).
Edge Cases & Behavior
- First boot: Nodes need at least 1 peer and one complete commit-reveal round before
emitting pulses.
/public/latestreturns 503 until then. - Genesis pulse: Round 1 has
previous_signature: null. Clients must accept this as the chain origin. - Failed rounds: If fewer than 2 contributors reveal, no pulse is created for that round. Round numbering skips nothing — the next successful round increments normally.
- History pruning: Nodes retain only the most recent 1,000 pulses. Older rounds return 404.
- Clock skew: Timestamps come from AGUWA (mesh-synchronized time), not the OS clock.
A node with a GPS MA-902 may show an offset of ±1–2 seconds. Clients should use
2 × periodas the freshness threshold, not strict wall-clock time. - Cross-node pulses: In a mesh, different nodes may produce pulses with the same round
number but different randomness (each node runs its own commit-reveal). Always verify against the
public_keyembedded in the pulse.
Quick Verification Script
# Node.js (ships with yakmesh-node)
npm run verify:beacon https://time.yakmesh.dev
# Or use the Python version
python3 scripts/verify_beacon.py https://time.yakmesh.dev
Version History
| Version | Changes |
|---|---|
| v3.3.0 | Complete rewrite: SHA3-256 sponge engine replaces 144-slot array. 5 hardware entropy sources, two-source extractor, pluggable registry, ~128 byte state (was 4,608). Entropy Sentinel & TRIBHUJ verdicts retained. |
| v2.x | STEADYWATCH seed store: 144 quantum seeds, Hurwitz quaternion simulation, SST families, Fibonacci-cycle selection, batch quality consensus. |
| v1.0 | Initial seed loading and hybrid seed algorithm. |