.NET implementation of collision-resistant ids
A .NET implementation of collision-resistant unique identifiers (CUIDs) designed for horizontal scalability and security in distributed environments. This library provides robust alternatives to traditional GUIDs with improved readability, sortability, and security characteristics.
A command-line utility, cuidgen, is also available for leveraging CUIDs in scripting environments.
VISLIB0001 to encourage migrationInstall cuid.net via NuGet Package Manager:
dotnet add package cuid.netOr via the Package Manager Console:
Install-Package cuid.netSupported Platforms:
Dependencies:
The library automatically includes the following runtime dependencies via NuGet:
All platforms:
.NET Standard 2.0/2.1 only:
[!NOTE] While .NET Framework 4.6.1 is the minimum supported version, .NET Framework 4.7.2+ is recommended for optimal compatibility.
using Visus.Cuid;
// CUIDv2 (Recommended)
Cuid2 id = new Cuid2();
Console.WriteLine(id); // o2tm13zgjtaur83duiakvgiq
// CUIDv2 with custom length
Cuid2 shortId = new Cuid2(10);
Console.WriteLine(shortId); // rolaz6ek3u
// CUIDv1 (Deprecated - emits compiler warning VISLIB0001)
Cuid legacyId = Cuid.NewCuid();
Console.WriteLine(legacyId); // cmjj07yka00016337xrs9mj24[!NOTE]
Cuid2is the recommended implementation for all new projects. It provides cryptographically strong identifiers suitable for security-sensitive contexts.
Cuid2 is an immutable structure that generates collision-resistant identifiers using SHA-3 hashing. Unlike CUIDv1, it is designed with security as a primary concern and does not leak information about generation time or location.
IComparable for securityIEquatable<Cuid2> for comparisons.ToString() for string representationCUIDv2 values use a variable-length structure with no predefined pattern. The generation process:
Input Components:
Hash Computation: All components except the prefix are hashed using SHA-3 512-bit
Encoding: Hash is base-36 encoded, then truncated to requested length minus 1, with the random prefix prepended
Example:
o2tm13zgjtaur83duiakvgiq
using Visus.Cuid;
// Default length (24 characters)
Cuid2 id = new Cuid2();
Console.WriteLine(id); // o2tm13zgjtaur83duiakvgiq
// Custom length (4-32 characters)
Cuid2 shortId = new Cuid2(10);
Console.WriteLine(shortId); // v1888wvo9i
Cuid2 longId = new Cuid2(32);
Console.WriteLine(longId); // zkx5dng1v8r0dg36id29uoqt1dsndmvbusing Visus.Cuid;
Cuid2 id = new Cuid2();
// Explicit conversion
string idString = id.ToString();
// Implicit conversion
string implicit = id;using Visus.Cuid;
Cuid2 id1 = new Cuid2();
Cuid2 id2 = new Cuid2();
Cuid2 id3 = id1;
// Equality operators
bool areEqual = id1 == id3; // true
bool notEqual = id1 != id2; // true
// Equals method
bool equals = id1.Equals(id3); // true
// GetHashCode support for collections
HashSet<Cuid2> uniqueIds = new HashSet<Cuid2> { id1, id2, id3 };
Console.WriteLine(uniqueIds.Count); // 2using Visus.Cuid;
// Default value
Cuid2 defaultId = default;
Cuid2 emptyId = new Cuid2(0); // Creates empty instance
// Check for empty
bool isEmpty = string.IsNullOrEmpty(defaultId.ToString());[!IMPORTANT] Technical Details:
- The fingerprint size is variable (depends on hostname length and environment variables)
- The random data size matches the requested identifier length
- The timestamp precision is in ticks (100-nanosecond intervals), not milliseconds
- SHA-3 is the NIST-standardized algorithm (FIPS 202), not the original Keccak submission
CUIDv2 validates length during construction:
using Visus.Cuid;
try
{
// Invalid: length must be between 4 and 32
Cuid2 tooShort = new Cuid2(3); // throws ArgumentOutOfRangeException
Cuid2 tooLong = new Cuid2(33); // throws ArgumentOutOfRangeException
}
catch (ArgumentOutOfRangeException ex)
{
Console.WriteLine($"Invalid length: {ex.Message}");
}
// Valid lengths
Cuid2 valid1 = new Cuid2(4); // Minimum
Cuid2 valid2 = new Cuid2(24); // Default
Cuid2 valid3 = new Cuid2(32); // Maximum[!CAUTION] CUIDv1 has been deprecated for security reasons. Migrate to
Cuid2for all new projects and security-sensitive applications.
[!WARNING] It is possible to derive with a degree of certainty when and where a CUIDv1 was created, making it unsuitable for security-sensitive contexts.
[!NOTE] Usage of CUIDv1 will emit the compiler warning
VISLIB0001to encourage migration to CUIDv2.
Cuid is an immutable structure designed for horizontal scaling and binary searches. It provides a sortable, "string-safe" alternative to Guid for scenarios where chronological ordering is needed and security is not a primary concern.
CUIDv1 should be avoided when:
CUIDv1 may be acceptable for:
CUIDv1 values are composed of several data points, base-36 encoded to a fixed 25-character length:
Example:
cmjj07yka00016337xrs9mj24
| Segment | Length | Source |
|---|---|---|
c | 1 | CUIDv1 identifier prefix |
mjj07yka | 8 | Unix timestamp in milliseconds (base-36) |
0001 | 4 | Session counter (base-36) |
6337 | 4 | Client fingerprint (process ID + hostname) |
xrs9mj24 | 8 | Random data (base-36) |
Total Length: 25 characters
using Visus.Cuid;
// Static factory method (recommended)
Cuid id = Cuid.NewCuid();
Console.WriteLine(id); // cmjj07yka00016337xrs9mj24
// Empty/default value
Cuid empty = Cuid.Empty;using Visus.Cuid;
// Constructor parsing
Cuid id1 = new Cuid("cmjj07yka00016337xrs9mj24");
// Explicit parsing
Cuid id2 = Cuid.Parse("cmjj07yka00016337xrs9mj24");
// Try-parse pattern
if (Cuid.TryParse("cmjj07yka00016337xrs9mj24", out Cuid id3))
{
Console.WriteLine($"Parsed: {id3}");
}
else
{
Console.WriteLine("Invalid CUID format");
}Cuid implements IComparable, IComparable<Cuid>, and IEquatable<Cuid>:
using Visus.Cuid;
Cuid id1 = Cuid.NewCuid();
Thread.Sleep(10); // Ensure different timestamp
Cuid id2 = Cuid.NewCuid();
// Comparison operators
bool isLess = id1 < id2; // true (earlier timestamp)
bool isGreater = id2 > id1; // true
bool areEqual = id1 == id1; // true
// CompareTo method
int comparison = id1.CompareTo(id2); // -1 (id1 is earlier)
// Sorting
List<Cuid> ids = new List<Cuid> { id2, id1 };
ids.Sort(); // Chronological order: [id1, id2]
// Empty comparison
bool isEmpty = id1 == Cuid.Empty; // falseusing Visus.Cuid;
Cuid id1 = Cuid.Parse("cmjj07yka00016337xrs9mj24");
Cuid id2 = Cuid.Parse("cmjj07yka00016337xrs9mj24");
Cuid id3 = Cuid.NewCuid();
// Equality operators
bool equal = id1 == id2; // true
bool notEqual = id1 != id3; // true
// Equals method
bool equals = id1.Equals(id2); // true
// Hash code support
Dictionary<Cuid, string> lookup = new Dictionary<Cuid, string>
{
{ id1, "First" },
{ id3, "Second" }
};CUIDv1 provides built-in serialization support for JSON and XML.
using System.Text.Json;
using Visus.Cuid;
// Serialize
Cuid id = Cuid.NewCuid();
string json = JsonSerializer.Serialize(id);
Console.WriteLine(json); // "cmjj07yka00016337xrs9mj24"
// Deserialize
Cuid deserialized = JsonSerializer.Deserialize<Cuid>("\"cmjj07yka00016337xrs9mj24\"");
// In objects
public class Document
{
public Cuid Id { get; set; }
public string Content { get; set; }
}
Document doc = new Document
{
Id = Cuid.NewCuid(),
Content = "Example"
};
string docJson = JsonSerializer.Serialize(doc);
// {"Id":"cmjj07yka00016337xrs9mj24","Content":"Example"}using System.Xml;
using System.Xml.Serialization;
using Visus.Cuid;
// Serialize
Cuid id = Cuid.NewCuid();
XmlSerializer serializer = new XmlSerializer(typeof(Cuid));
XmlWriterSettings settings = new XmlWriterSettings { Indent = false };
using (StringWriter sw = new StringWriter())
using (XmlWriter xw = XmlWriter.Create(sw, settings))
{
serializer.Serialize(xw, id);
Console.WriteLine(sw.ToString());
// <?xml version="1.0" encoding="utf-16"?><cuid>cmjj07yka00016337xrs9mj24</cuid>
}
// Deserialize
string xml = "<?xml version=\"1.0\" encoding=\"utf-16\"?><cuid>cmjj07yka00016337xrs9mj24</cuid>";
using (StringReader sr = new StringReader(xml))
using (XmlReader xr = XmlReader.Create(sr))
{
Cuid deserialized = (Cuid)serializer.Deserialize(xr);
}cuid.net targets multiple frameworks for broad compatibility:
| Target Framework | Version |
|---|---|
| .NET Standard | 2.0, 2.1 |
| .NET | 8.0, 10.0 |
| .NET Framework | 4.6.1+ (via .NET Standard 2.0) |
[!NOTE] While .NET Framework 4.6.1 is the minimum supported version, .NET Framework 4.7.2 or later is recommended for optimal .NET Standard 2.0 compatibility.
C# Language Features:
Trimming Support:
IsTrimmable=true)Dependencies:
All platforms:
.NET Standard 2.0/2.1 additional packages:
CUIDv2 uses cryptographic operations (SHA-3 512-bit) for security, which has performance implications:
Generation Speed:
Memory Allocation:
Optimization Tips:
CUIDv1 is faster than CUIDv2 due to simpler generation:
Generation Speed:
Trade-offs: