The 30-second summary

Every modern runtime ships a CSPRNG: crypto.randomUUID() and crypto.getRandomValues() in browsers and Node.js, secrets in Python, SecureRandom in the JVM. They're fast, free, and cryptographically strong. For session IDs, CSRF tokens, password salts, request IDs — anything where the only consumer of the randomness is your own server — they're the correct answer.

They become the wrong answer the moment a second party has to verify that the result you reported is the result your code actually produced. An in-process CSPRNG is unobservable from the outside; there is no artifact a third party can check.

Feature matrix

CapabilityProvable.iocrypto.randomUUID() / getRandomValues()
Cryptographic strengthHMAC-SHA256 keyed streamOS CSPRNG (e.g. /dev/urandom, BCryptGenRandom)
Reproducible from seedYes — that's the whole pointNo — fresh entropy on every call
Third-party verifiabilityYes — publish seed + hash, anyone re-derivesNo — the call is invisible from outside the process
Pre-commitmentYes — serverHash published before the resultNo
Latency~tens of ms (network)Sub-microsecond (in-process)
Auditable historyYes — every outcome persisted and addressable by short IDNo — gone the moment the process exits
External dependencyYes — an HTTP callNo — language-level
Best forAnything a user, auditor, or regulator might disputeInternal tokens, IDs, salts, padding, jitter

When crypto.randomUUID() is the right answer

Don't reach for an HTTP API when a local function call does the job. Stick with the in-process CSPRNG for:

When you actually need a verifiable RNG

The "who needs to check this?" question is the whole tell. If the answer is anyone outside your own process, the local CSPRNG can't help — there's nothing to publish. Reach for Provable.io when:

What the API call looks like

Same shape as a Crypto API call, just over HTTP — and the response carries the proof in addition to the value.

curl "https://api.provable.io/api/bytes?clientSeed=compare-crypto-demo&count=16&encoding=hex"

The response includes a serverHash. Anyone with the seed and that hash can confirm — via the open-source provable-core library — that the bytes you reported are the bytes the algorithm produced.

A common mistake: using both for the same job

We see two anti-patterns regularly:

  1. Calling Provable.io for session tokens. You don't need verification, and you're adding a network round trip to every login. Use the language CSPRNG.
  2. Calling crypto.randomUUID() for a public raffle winner. The draw happens, the winner is announced, the runners-up ask "how do we know?" — and there's no answer. Use Provable.io and publish the serverHash.

The line is about audience, not about strength. Both primitives are cryptographically sound; they answer different questions.

FAQ

Is Provable.io a CSPRNG?

Its output is computationally indistinguishable from a CSPRNG, but the trust model is different: it's verifiable rather than unobservable. That's a feature for public-audience use cases and a footgun for secret material. Never use the same Provable.io seed for both visible draws and secret keys.

What about Math.random()?

Math.random() is not cryptographically strong and shouldn't be used for either job. If a value matters, use crypto.getRandomValues() for in-process needs and Provable.io for verifiable ones.

Can I cache outcomes?

Yes. Because the call is deterministic in (serverSeed, clientSeed, cursor, nonce), you can cache the result keyed by the inputs. The seed-based design is what makes both verification and caching cheap.

Next steps