Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

SecureRandom unstable

Cryptographically secure randomness: random bytes, uniform integers, shuffles, UUIDv4, UUIDv7, and ULIDs.

The SecureRandom service is built from one randomBytes primitive with make. layerWebCrypto draws from getRandomValues on the WebCrypto reference. The *Unsafe functions draw synchronously from globalThis.crypto for code that cannot use a service, such as constructors and model defaults.

Every operation has a never error channel. A bad size or an empty range is a defect.

Gotchas

There are no float, boolean, or full-range integer helpers. Insecure random numbers belong to effect/Random, and a secure bit is randomBytes(1).

8 exports Since v4.0.0 Source

Constructors

make

unstable
Since v4.0.0

Creates a SecureRandom service from a secure byte source.

When to use

Use to build the service for a platform integration, a custom runtime, or a seeded test double.

Details

Every operation is derived from randomBytes. Sizes are checked before it is called, and an exception it throws is a defect. Each service orders its own UUIDv7s and ULIDs, independently of other services and of the *Unsafe functions.

Gotchas

randomBytes must return a fresh array of exactly size secure random bytes. UUID formatting mutates the returned array.

Signature

declare function make(randomBytes: (size: number) => Uint8Array): {
readonly intBetween: (min: number, max: number, options?: {
readonly halfOpen?: boolean;
}) => Effect<number>;
readonly randomBytes: (size: number) => Effect<Uint8Array<ArrayBufferLike>>;
readonly shuffle: <A>(elements: Iterable<A>) => Effect<Array<A>>;
readonly ulid: Effect<string>;
readonly uuidV4: UuidGenerator;
readonly uuidV7: UuidGenerator;
}

Example Creating a SecureRandom service

import { Effect } from "effect"
import { SecureRandom } from "effect/crypto"
const random = SecureRandom.make((size) => globalThis.crypto.getRandomValues(new Uint8Array(size)))
const bytes = await Effect.runPromise(random.randomBytes(4))
bytes.length // => 4

Layers

Since v4.0.0

Layer that provides SecureRandom from getRandomValues on the WebCrypto reference.

Details

Requests above the 65,536-byte getRandomValues limit are filled in chunks. Only getRandomValues is needed, so the layer also works in insecure browser contexts, which have no crypto.subtle.

Signature

declare const layerWebCrypto: Layer.Layer<SecureRandom>

Models

UuidGenerator

interface unstable
Since v4.0.0

Generates a UUID as a lowercase, hyphenated string or, with format: "bytes", the 16 UUID bytes.

Signature

interface UuidGenerator {
(options?: {
readonly format?: "string";
}): Effect<string>;
<Format extends "string" | "bytes">(options: {
readonly format: Format;
}): Effect<Format extends "bytes" ? Uint8Array<ArrayBufferLike> : string>;
}

Services

Since v4.0.0

Service that generates cryptographically secure random values.

Details

Every operation is derived from randomBytes. See make.

Signature

declare class SecureRandom extends Shape<"effect/crypto/SecureRandom", {
readonly intBetween: (min: number, max: number, options?: {
readonly halfOpen?: boolean;
}) => Effect<number>;
readonly randomBytes: (size: number) => Effect<Uint8Array<ArrayBufferLike>>;
readonly shuffle: <A>(elements: Iterable<A>) => Effect<Array<A>>;
readonly ulid: Effect<string>;
readonly uuidV4: UuidGenerator;
readonly uuidV7: UuidGenerator;
}, this> {
constructor(_: never);
}

Unsafe

Since v4.0.0

Synchronously generates size cryptographically secure random bytes from globalThis.crypto.

When to use

Use when a service cannot be read, as in constructors and model defaults. Elsewhere, use the SecureRandom service.

Gotchas

Reads globalThis.crypto directly, so neither the WebCrypto reference nor a mocked SecureRandom service affects it. A negative or non-integer size throws a RangeError.

Signature

declare function randomBytesUnsafe(size: number): Uint8Array

ulidUnsafe

unstable
Since v4.0.0

Synchronously generates a ULID from Date.now() and globalThis.crypto.

Details

Below the maximum timestamp, every ULID generated in this JS realm (process or worker) sorts after the previous one, using the monotonic mode described on the SecureRandom service's ulid. The ordering does not hold across processes or workers. Consecutive ULIDs reveal their generation order, so never use them as secrets.

Gotchas

Reads Date.now() rather than the Clock, and globalThis.crypto rather than the WebCrypto reference. A timestamp can run slightly ahead of Date.now() when the clock moves back or the random part overflows.

Signature

declare function ulidUnsafe(): string
Since v4.0.0

Synchronously generates a UUIDv4 from globalThis.crypto, as a lowercase hyphenated string or, with format: "bytes", the 16 UUID bytes.

Gotchas

Reads globalThis.crypto directly, so neither the WebCrypto reference nor a mocked SecureRandom service affects it.

Signature

declare const uuidV4Unsafe: {
(options?: {
readonly format?: "string";
}): string;
<Format extends "string" | "bytes">(options: {
readonly format: Format;
}): Format extends "bytes" ? Uint8Array<ArrayBufferLike> : string;
}
Since v4.0.0

Synchronously generates a UUIDv7 from Date.now() and globalThis.crypto, as a lowercase hyphenated string or, with format: "bytes", the 16 UUID bytes.

Details

Below the maximum timestamp, every UUIDv7 generated in this JS realm (process or worker) sorts after the previous one, as a string and as bytes, using the counter described on the SecureRandom service's uuidV7. The ordering is shared with Model.UuidV7WithGenerate, and does not hold across processes or workers. Consecutive UUIDv7s reveal their generation order, so never use them as secrets.

Gotchas

Reads Date.now() rather than the Clock, and globalThis.crypto rather than the WebCrypto reference. A timestamp can run slightly ahead of Date.now() when the clock moves back or the counter overflows.

Signature

declare const uuidV7Unsafe: {
(options?: {
readonly format?: "string";
}): string;
<Format extends "string" | "bytes">(options: {
readonly format: Format;
}): Format extends "bytes" ? Uint8Array<ArrayBufferLike> : string;
}