Open-source project
paralleldrive/cuid2 avatar
paralleldrive/cuid2

paralleldrive/cuid2: secure collision-resistant ids for horizontally scaled apps

The most secure, collision-resistant ids optimized for horizontal scaling and performance.

3,401 stars73 forksJavaScriptMIT

At a glance

What is it?
Cuid2 is an MIT-licensed JavaScript id generator that hashes multiple entropy sources with SHA3 instead of trusting a single CSPRNG. It fits distributed apps that need unguessable, URL-safe ids; it does not fit sortable keys or tight render loops.
Who is it for?
Adopt Cuid2 when ids are exposed in URLs, sessions or public APIs and you generate them on more than one host without coordination. Skip it when you need k-sortable keys, database-friendly sequential ids, or id generation inside render loops, where the README itself points to a plain counter, Ulid or NanoId.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 49 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem Cuid2 solves: ids that leak nothing and collide nowhere

Most applications pick an id format once and never revisit it. Cuid2 exists because that default choice has consequences. The README frames ids as something that should be secure by default, the same way browser sessions are, and it lists concrete failure modes: unauthorized account access, unauthorized access to user data, and accidental leaks of personal data. The examples it cites include the 2018 Strava heatmap incident and PleaseRobMe, both cases where an identifier or a derived artifact revealed more than intended.

The audience is specific. Cuid2 targets applications that generate ids on multiple machines without coordination, need those ids to be unguessable, and want them safe to put in URLs and names. Offline generation is supported, so a client or an edge worker can mint an id without a round trip. The README is equally direct about who should look elsewhere: anyone who needs sequential ids, or who generates ids inside high performance tight loops such as render loops.

How Cuid2 generates an id: multiple entropy sources hashed with SHA3

The mechanism is a hash, not a counter. The README states that Cuid2 uses multiple, independent entropy sources and hashes them with a security-audited, NIST-standard cryptographically secure hashing algorithm, Sha3. That combination is the whole design: no single random source has to be trustworthy, because the output is a digest of several inputs.

That choice explains the library's stance on speed. The README argues that if you can hash too quickly you can launch parallel attacks to find duplicates or break entropy-hiding, and concludes that for unique ids the fastest runner loses the security race. So Cuid2 is deliberately not the fastest generator available, and the README claims it stays under 5k gzipped and performs no async operations.

The default output length is 24 characters. The README gives a collision estimate: you would need roughly 4,000,000,000,000,000,000 ids to reach a 50 percent chance of collision, derived from the birthday problem approximation sqrt(36^(24-1) * 26). Ids use no special characters, which is what makes them URL and name friendly.

A fingerprint is part of the configuration surface. The README describes it as a custom fingerprint for the host environment, used to help prevent collisions when generating ids in a distributed system. That is the coordination-free part: hosts do not talk to each other, but they can be distinguished.

Installing @paralleldrive/cuid2 and generating your first ids

The package installs from npm or Yarn. The README gives both forms.

bash
npm install --save @paralleldrive/cuid2

Import createId and call it. The README's programmatic example produces three ids in sequence, each a 24-character string like tz4a98xxat96iws9zmbrgj3a.

js
import { createId } from "@paralleldrive/cuid2";

const ids = [
  createId(), // 'tz4a98xxat96iws9zmbrgj3a'
  createId(), // 'pfh0haxfpzowht3oi213cqos'
  createId(), // 'nc6bzmkmd014706rfda898to'
];

If you need a different length, a custom random function, or a host fingerprint, use init. It returns a configured createId function, and the README notes all configuration properties are optional.

js
import { init } from "@paralleldrive/cuid2";

const createId = init({
  random: Math.random,
  length: 10,
  fingerprint: "a-custom-host-fingerprint",
});

Validation is a separate export. isCuid returns true for a generated id and false for a string that is not one.

js
import { createId, isCuid } from "@paralleldrive/cuid2";

console.log(
  isCuid(createId()), // true
  isCuid("not a cuid") // false
);

There is also a CLI. The README recommends installing a shell alias first, then generating ids from a terminal.

bash
npx @paralleldrive/cuid2 --install
npx @paralleldrive/cuid2 --help
cuid 5
cuid --slug
cuid --length 10
cuid --fingerprint "my-server" 2

The command takes an optional count argument, defaulting to 1, plus --slug for a short 5-character id, --length <n>, --fingerprint <s>, --install and --help. Combining options is allowed: the README shows cuid --length 8 --fingerprint "test".

Where Cuid2 is the wrong tool: sorted keys and hot loops

Cuid2 is not k-sortable. The README says so plainly and links to a note on k-sortable, sequential or monotonically increasing ids, and lists sequential ids first under what Cuid2 is not good for. If your schema relies on ids that sort by creation time, or you use the id as a database clustering key, Cuid2 will not give you that ordering. This is the trade-off of hiding entropy: a value that reveals nothing about when it was made cannot also encode time.

The second limitation is throughput. The README excludes high performance tight loops such as render loops, and suggests a simple counter when you do not need cross-host uniqueness or security, or Ulid and NanoId as alternatives. Note the reason is not that Cuid2 is slow in absolute terms; the README claims no user-noticeable delays and no async operations. It is that the design intentionally caps hashing speed to keep entropy-hiding meaningful.

A third constraint is entropy supply. The configuration accepts a custom random function with the same API as Math.random, and the README's own example passes Math.random. That example is illustrative of the API shape, but the README's broader argument warns against trusting a single CSPRNG, citing browser CSPRNG bugs and the history of Chromium's Math.random. If you override random with a weak source, you are replacing part of the guarantee.

Finally, the README does not document rollback, id migration, or how to handle ids already issued under a different length or fingerprint. Changing length or fingerprint mid-flight is not addressed.

Cuid2 vs UUID, NanoID and Ulid: what actually differs

The README positions Cuid2 against UUIDs and GUIDs directly: forget them, it says, because they often collide in large apps. The stated difference is entropy sourcing. Cuid2 combines several independent entropy sources and hashes them with SHA3, while the README argues it is not a good idea to trust a browser's cryptographically secure pseudo random number generator, the mechanism used in tools like uuid and nanoid. It cites bugs in browser CSPRNGs and the years when Chromium's Math.random was not very random as the motivation. So the contrast with uuid and NanoID is not output format or speed; it is how much trust is placed in one entropy source.

Against Ulid, the difference is ordering. Ulid appears in the README only as a suggestion for tight loops where you do not need cross-host unique ids or security. Ulid's lexical sortability is exactly the property Cuid2 gives up, and Cuid2's entropy-hiding is exactly what a time-prefixed id cannot offer. Pick based on whether you need sortable keys or unguessable ones.

NanoId is the closer comparison, since it also produces short URL-safe strings. The README's objection is the entropy source, not the length. Both are small; Cuid2's README claims under 5k gzipped. If your threat model does not include id guessing, the extra hashing buys you little.

Maintenance, releases and licence

The repository is not archived, and the last push was on 2026-08-12. The package is published as @paralleldrive/cuid2 with the MIT licence, author Eric Elliott. The MIT terms matter for adoption: you can use, modify and redistribute it, including in closed products, provided the copyright notice and permission notice are preserved. That is a summary of the licence identifier, not legal advice; read the LICENSE file in the repository for the binding text.

Release tooling is visible in the repository layout. There is a release.js script and a .release-it.json config, and package.json defines a release script that runs node release.js. The README does not document a release cadence, so there is no stated schedule to plan upgrades around. No recent releases were retrieved for this review, which means version history is not something to rely on here.

The published artifact is small and narrowly scoped: package.json lists files as src/index.js, index.js, index.d.ts, bin/cuid2.js. TypeScript types ship with the package via index.d.ts, so no separate @types package is needed. The bin entry maps cuid2 to ./bin/cuid2.js, which is what the CLI invocations resolve to. The package is ESM (type: module) with an exports map exposing the root and ./package.json. If your project is CommonJS-only, that export shape is the first thing to check before adopting.

A fingerprint per host, and what to verify before shipping

The single most consequential configuration decision is the fingerprint. The README describes it as a custom fingerprint for the host environment, used to help prevent collisions when generating ids in a distributed system. If every host passes the same fingerprint, you have removed one of the independent inputs the design relies on. If each host passes a distinct one, the ids from different machines carry a distinguishing component without any coordination.

What the README does not say is how the fingerprint is derived by default, or whether it is stable across restarts and deploys. That is worth confirming in src/index.js before you depend on it, especially if hosts are ephemeral containers. The README also does not document what happens when you change the fingerprint on a running system, or how existing ids behave under a new one.

The second thing to verify is the random function in your target runtime. The init example passes Math.random, and the README's security argument is built on not trusting a single CSPRNG. If you supply your own random, confirm it is cryptographically secure in every environment you ship to, including browsers and edge runtimes. Validation is cheap to check as well: isCuid(createId()) returning true, and isCuid("not a cuid") returning false, is the README's own smoke test.

Editorial conclusion

Adopt Cuid2 when ids are exposed in URLs, sessions or public APIs and you generate them on more than one host without coordination. Skip it when you need k-sortable keys, database-friendly sequential ids, or id generation inside render loops, where the README itself points to a plain counter, Ulid or NanoId. Before rolling it out, verify the fingerprint strategy for your hosts and confirm your runtime supplies the entropy the library expects.

Frequently asked questions

What is the difference between a UUID and a Cuid?

The README says UUIDs and GUIDs often collide in large apps, while Cuid2 combines multiple independent entropy sources and hashes them with SHA3. Cuid2 output is 24 characters by default and uses no special characters, so it is URL and name friendly.

What is the difference between cuid2 and cuid?

The README describes Cuid2 as the next generation of Cuid, created to address untrustworthy entropy in id generators that led to frequent id collisions. It states that Cuid2 uses multiple independent entropy sources hashed with SHA3, and that it is not feasible to guess the next id or learn anything about the referenced data from an id.

How do I install and use @paralleldrive/cuid2?

Install it with npm install --save @paralleldrive/cuid2, then import createId and call it to get ids such as tz4a98xxat96iws9zmbrgj3a. For custom length, a custom random function or a host fingerprint, use init, which returns a configured createId function.

How long is a cuid2 id, and can I change the length?

The default length is 24 characters. The README shows changing it through init with a length property, and through the CLI with --length <n>, for example cuid --length 10. There is also --slug for a short 5-character id.

Is cuid2 suitable for sortable or sequential database keys?

No. The README lists sequential ids first under what Cuid2 is not good for and links to a note on k-sortable, sequential or monotonically increasing ids. If you need sortable keys, the design goal of hiding entropy works against you.

Can I use the cuid2 CLI without installing the package globally?

Yes. The README shows npx @paralleldrive/cuid2 --install to add a shell alias, after which you run cuid, cuid 5, cuid --slug or cuid --length 10. Without the alias, the full command is npx @paralleldrive/cuid2.

Official sources

  1. Issues
  2. License: MIT
  3. paralleldrive/cuid2 on GitHub
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/paralleldrive-cuid2.svg)](https://hysenlabs.com/projects/paralleldrive-cuid2)