A compact, sortable, and readable identifier library.
Find a file
Oliver Mitchell 7cecd7604e
All checks were successful
/ build (push) Successful in 47s
Empty
2026-04-30 14:49:41 +09:30
.forgejo/workflows Release candidate 2026-04-30 12:42:39 +09:30
PolymerLabs.Druid Added core and generators 2026-04-29 23:51:19 +09:30
PolymerLabs.Druid.Benchmarks Release candidate 2026-04-30 12:42:39 +09:30
PolymerLabs.Druid.Examples Release candidate 2026-04-30 12:42:39 +09:30
PolymerLabs.Druid.Generators Added core and generators 2026-04-29 23:51:19 +09:30
PolymerLabs.Druid.Generators.Tests Release candidate 2026-04-30 12:42:39 +09:30
PolymerLabs.Druid.Tests Release candidate 2026-04-30 12:42:39 +09:30
.gitignore First commit of gitignore 2026-04-29 22:48:19 +09:30
PolymerLabs.Druid.sln Release candidate 2026-04-30 12:42:39 +09:30
README.md Added core and generators 2026-04-29 23:51:19 +09:30

Druid

Druid is a compact identifier library for .NET. A Druid packs a domain, shard, timestamp, and sequence into one 64-bit value and offers serialization to a 11-character base64url string.

DuIKCSHQ5bB
UuIKCSHQ5bB

The first character is the domain. That keeps IDs readable in logs, URLs, traces, and API payloads without expanding the stored value.

Packages

Package Use it for
Druid Provides the Druid value type, also with component types, parsing, formatting, comparison, and conversion.
Druid.Generators Configurable Druid generation tooling.

Installation

dotnet add package Druid
dotnet add package Druid.Generators

Quick Start

using PolymerLabs.Druid;
using PolymerLabs.Druid.Generators;

var generator = new DruidGeneratorBuilder()
    .Build();

Druid userId = generator.Create(Domain.U);
Druid designId = generator.Create(Domain.D);

Console.WriteLine(userId);   // UuI5fD79cb7
Console.WriteLine(designId); // DuIKCSHQ5bB

Use domains for the type of thing being identified. Use shards for the source that generated the ID, such as a service, worker, tenant, import job, or region.

Use Cases

  • Public IDs where IDs need to be short and readable.
  • Multi-entity systems where ID types need to be easily distinguishable.
  • Distributed, concurrent generators across multiple generation instances.
  • Database keys or indexed columns where size and sortability matters.

String Form

Druid strings are 11 characters long and use the base64url alphabet: A-Z, a-z, 0-9, -, and _.

using PolymerLabs.Druid;
using PolymerLabs.Druid.Parts;

var timestamp = Timestamp.Create(30541986);
var sequence = Sequence.Create(23525);

var designId = Druid.Create(Domain.D, Shard.Create(46), timestamp, sequence);
var userId = Druid.Create(Domain.U, Shard.Create(46), timestamp, sequence);

Console.WriteLine(designId); // DuIKCSHQ5bB
Console.WriteLine(userId);   // UuIKCSHQ5bB

Only the first character changed because only the domain changed. That makes mixed-domain data easier to read:

User   UuIKCSHQ5bB
Design DuIKCSHQ5bB

Domain checks can be made explicitly:

var id = Druid.ParseExact("UuIKCSHQ5bB");

if (id.Domain != Domain.U)
{
    throw new ArgumentException("Expected a user ID.", nameof(id));
}
```:wq:

## Layout

A Druid is packed into 64 bits:

```text
[ sequence:18 ][ timestamp:32 ][ shard:8 ][ domain:6 ]
Part Size Purpose
Domain 6 bits Separates IDs by type. Examples: Domain.U for users, Domain.D for designs.
Shard 8 bits Identifies the generator source. Use it for nodes, regions, tenants, workers, or import streams.
Timestamp 32 bits Stores seconds since the configured epoch.
Sequence 18 bits Adds per-generator variation for the same timestamp.

The packed value is 8 bytes. The string value is 11 characters.

Generating IDs

Druid.Generators provides DruidGeneratorBuilder for application code:

using PolymerLabs.Druid.Generators;
using PolymerLabs.Druid.Parts;

var generator = new DruidGeneratorBuilder()
    .UseShard(Shard.Create(12))
    .UseEpoch(DateTimeOffset.UnixEpoch)
    .UseIncrementalSequencer()
    .Build();

var userId = generator.Create(Domain.U);
var userIds = generator.Create(Domain.U, count: 10).ToArray();

Available generator options:

Option Purpose
UseShard(Shard.Create(value)) Sets the shard for generated IDs. Defaults to Shard.First.
UseEpoch(DateTimeOffset) Sets the epoch used for timestamp calculation. Defaults to Unix epoch.
UseTimeProvider(TimeProvider) Sets the time source for tests and deterministic imports.
UseIncrementalSequencer() Uses ascending sequence values. This is the default.
UseRandomSequencer() Uses random sequence values.
UseRandomSequencer(seed) Uses deterministic random sequence values from a seed.
UseSequencer<T>() / UseSequencer(instance) Uses a custom ISequencer.

Manual construction is available when the exact parts matter:

var id = Druid.Create(
    Domain.U,
    Shard.Create(5),
    Timestamp.Create(new DateTimeOffset(2026, 4, 29, 0, 0, 0, TimeSpan.Zero)),
    Sequence.Create(563));

Parsing and Storage

var id = Druid.ParseExact("DuIKCSHQ5bB");

string text = id.ToString();        // "DuIKCSHQ5bB"
ulong packed = id.ToUnsignedInt64();
long signed = id.ToSignedInt64();
byte[] bytes = id.ToByteArray();

Use ParseExact when the input must contain only the ID. Use TryParseExact for non-throwing validation.

For storage, use the 64-bit value for compact database columns. ToSignedInt64() provides an order-preserving signed long representation for systems that store signed integer columns.

Examples

See the examples project for complete usage:

License

No license file is currently included in this repository.