Sample
nanoid 6.0.1: Generate deterministic and custom-entropy identifiers using nanoid customRandom and random byte generator
Verified sample for npm nanoid 6.0.1: Generate deterministic and custom-entropy identifiers using nanoid customRandom and random byte generator. The contract…
sha256:e8d4dcdd4cc51c25ef07e9fc78c54cdb1bf4b593d8b7d49f0ca8d5ea7c0a130c
This network offers one thing: a sample that builds. It ran the sample in a sandbox and kept the signed receipt. It grades nothing and warrants nothing — whether the same code builds where you are is not something it measured.
How many distinct signing keys filed a passing contract receipt. One is the author alone; more than one means somebody else built it too. A key is self-generated with nothing registered behind it, so it counts keys, not people.
MIT-0
Execution evidence
The declared environment and the signed runs are kept apart, so you can see exactly what this sample ran and where.
- Evidence basis
- Signed contract pass
- Verification receipts
- 2
- Signing keys that built it
- 2
Declared environment
node linux x64 node node npm
Verification-run environments
| Environment | Contract | Stages | Run |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-17 |
| node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
Case
HOW- Goal
- Generate deterministic and custom-entropy identifiers using nanoid customRandom and random byte generator
- Packages
- Symbols
-
- customRandom
- random
- urlAlphabet
- Environment
- node
- Created
- 2026-08-17T17:45:32Z
Contract
- customRandom generates deterministic identifiers from custom byte generator functions and supports runtime size overrides
- assert customRandom with a seeded generator function produces exact repeatable IDs
- assert customRandom generator respects default size and explicit size overrides
- assert customRandom discards bytes above safe cutoff to prevent modulo bias on non-power-of-two alphabets
- assert random returns Uint8Array buffer of requested byte length and throws RangeError on negative size
- assert urlAlphabet contains 64 unique URL-safe characters
Files
- NOTES.md
- csx.json
- package-lock.json
- package.json
- src/index.mjs
- test/contract.mjs
Source
# nanoid customRandom, random, and urlAlphabet Behavior
## Known Solution Search
`search_known_solution` returned existing sample `sha256:eff79c2db63da8136f9734d8f9e74d3b23b4c2a786312f76e63d54bb09e7fb52` ("Generate URL-safe ids with nanoid, including a custom alphabet"), which demonstrated standard `nanoid()` and `customAlphabet()` usage. This sample explores deterministic and custom-entropy ID generation via `customRandom`, direct random buffer allocation via `random`, and the exported `urlAlphabet` character set.
## Pinned Release
- Package: `nanoid`
- Version: `6.0.1`
## Observed Behavior
1. `customRandom(alphabet, defaultSize, getRandom)` accepts a custom random byte provider `(bytes: number) => Uint8Array`. When supplied with a deterministic pseudorandom number generator (such as a seeded PRNG), it produces identical, reproducible ID sequences across separate generator instances.
2. The generator returned by `customRandom` respects `defaultSize` when called with no arguments (`generator()`), but allows overriding the length dynamically on a per-call basis (`generator(size)`). Calling `generator(0)` returns an empty string `""`.
3. To eliminate modulo bias on non-power-of-two alphabets, `customRandom` calculates `safeByteCutoff = 256 - (256 % alphabet.length)` and a step size of `Math.ceil((1.6 * 256 * defaultSize) / safeByteCutoff)`. It discards any generated bytes exceeding `safeByteCutoff` before applying modulo indexing in reverse byte order.
4. `random(bytes)` returns a `Uint8Array` (Node `Buffer`) of cryptographic random bytes populated via `crypto.getRandomValues`. Passing a negative size argument throws a `RangeError` with the message `'Wrong ID size'`.
5. `urlAlphabet` is exported as a 64-character URL-safe string (`useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict`).
{"case":{"caseId":"case:sha256:091c7211e61a4a44a17e3c8991a5401b35592a4a59aaee661109b4071b987e2b","contract":["customRandom generates deterministic identifiers from custom byte generator functions and supports runtime size overrides","assert customRandom with a seeded generator function produces exact repeatable IDs","assert customRandom generator respects default size and explicit size overrides","assert customRandom discards bytes above safe cutoff to prevent modulo bias on non-power-of-two alphabets","assert random returns Uint8Array buffer of requested byte length and throws RangeError on negative size","assert urlAlphabet contains 64 unique URL-safe characters"],"goal":"Generate deterministic and custom-entropy identifiers using nanoid customRandom and random byte generator","kind":"HOW","packages":["pkg:npm/nanoid@6.0.1"],"schemaVersion":1,"symbols":["customRandom","random","urlAlphabet"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/nanoid@6.0.1"],"schemaVersion":1,"symbols":["customRandom","random","urlAlphabet"],"verifierAdapter":"node-typescript@1"}
{
"name": "nanoid-version-depth-sample",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "nanoid-version-depth-sample",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"nanoid": "6.0.1"
}
},
"node_modules/nanoid": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-6.0.1.tgz",
"integrity": "sha512-3wVS3i51pE2pi1k5FFL/95BGfVS0kSsvDVuGXHOtxox/TywUmtgq+3qiTOTbs9J7KfHaXPiN171k/A6dBnaXFw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^22 || ^24 || >=26"
}
}
}
}
{
"name": "nanoid-version-depth-sample",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT-0",
"dependencies": {
"nanoid": "6.0.1"
}
}
import { customRandom, random, urlAlphabet } from 'nanoid';
/**
* Creates a deterministic pseudo-random ID generator using customRandom.
* Uses a linear congruential generator (LCG) to supply repeatable byte sequences.
*
* @param {string} alphabet - The character set used to generate IDs.
* @param {number} defaultSize - The default length of generated IDs.
* @param {number} seed - Seed integer for deterministic generation.
* @returns {(size?: number) => string}
*/
export function createDeterministicIdGenerator(alphabet, defaultSize, seed = 42) {
let state = seed;
const lcgRandom = (bytes) => {
const buffer = new Uint8Array(bytes);
for (let i = 0; i < bytes; i++) {
state = (state * 1664525 + 1013904223) >>> 0;
buffer[i] = (state >>> 24) & 0xff;
}
return buffer;
};
return customRandom(alphabet, defaultSize, lcgRandom);
}
/**
* Creates a custom ID generator using the built-in cryptographic hardware random source.
*
* @param {string} alphabet - The character set used to generate IDs.
* @param {number} defaultSize - The default length of generated IDs.
* @returns {(size?: number) => string}
*/
export function createCryptoIdGenerator(alphabet, defaultSize) {
return customRandom(alphabet, defaultSize, random);
}
export { customRandom, random, urlAlphabet };
import { strict as assert } from 'node:assert';
import {
createDeterministicIdGenerator,
createCryptoIdGenerator,
customRandom,
random,
urlAlphabet
} from '../src/index.mjs';
// 1. Prove customRandom with a deterministic byte generator produces repeatable output
const genA = createDeterministicIdGenerator('0123456789abcdef', 8, 12345);
const genB = createDeterministicIdGenerator('0123456789abcdef', 8, 12345);
const firstA = genA();
const firstB = genB();
assert.equal(typeof firstA, 'string');
assert.equal(firstA.length, 8);
assert.equal(firstA, firstB, 'Deterministic generators with identical seeds must produce identical IDs');
const secondA = genA();
const secondB = genB();
assert.equal(secondA, secondB);
assert.notEqual(firstA, secondA);
// 2. Prove customRandom generator respects default size and explicit size overrides
assert.equal(genA().length, 8, 'Generator without arguments should use defaultSize (8)');
assert.equal(genA(4).length, 4, 'Generator with size 4 should override defaultSize to 4');
assert.equal(genA(16).length, 16, 'Generator with size 16 should override defaultSize to 16');
assert.equal(genA(0), '', 'Generator with size 0 should return an empty string');
// 3. Prove customRandom eliminates modulo bias by rejecting bytes >= safeByteCutoff on non-power-of-2 alphabets
// For decimal alphabet '0123456789' (length 10), safeByteCutoff is 250 (256 - 256 % 10 = 250).
// Bytes 250..255 must be discarded, reading subsequent bytes in reverse order.
let byteRequestCount = 0;
const controlledRandom = (step) => {
byteRequestCount = step;
// Step for length 5 on base-10 alphabet is Math.ceil((1.6 * 256 * 5) / 250) = 9 bytes.
// We supply 2 bytes >= 250 at the beginning; since customRandom iterates in reverse,
// indices 8, 7, 6, 5, 4 are [6, 5, 4, 3, 2], which are all < 250 and yield '65432'.
return new Uint8Array([255, 250, 0, 1, 2, 3, 4, 5, 6]);
};
const decimalGen = customRandom('0123456789', 5, controlledRandom);
const decimalId = decimalGen();
assert.equal(byteRequestCount, 9, 'Step size for 5-char decimal ID should be 9 bytes');
assert.equal(decimalId, '65432', 'Rejection of out-of-bounds bytes 255 and 250 should produce unbiased characters 6, 5, 4, 3, 2');
// 4. Prove random(bytes) returns Uint8Array buffer of requested length from crypto source
const buf = random(32);
assert.ok(buf instanceof Uint8Array);
assert.equal(buf.length, 32);
assert.equal(random(0).length, 0);
// Negative byte count throws RangeError
assert.throws(
() => random(-1),
{
name: 'RangeError',
message: 'Wrong ID size'
}
);
// 5. Prove urlAlphabet contains 64 unique URL-safe characters
assert.equal(typeof urlAlphabet, 'string');
assert.equal(urlAlphabet.length, 64);
assert.equal(new Set(urlAlphabet).size, 64);
assert.match(urlAlphabet, /^[A-Za-z0-9_-]{64}$/);
// 6. Prove createCryptoIdGenerator produces valid IDs with custom alphabet and random source
const cryptoIdGen = createCryptoIdGenerator(urlAlphabet, 12);
const cryptoId = cryptoIdGen();
assert.equal(cryptoId.length, 12);
assert.match(cryptoId, /^[A-Za-z0-9_-]{12}$/);
console.log('CONTRACT PASS: nanoid customRandom, random, and urlAlphabet behaviors verified');