One identity. Three representations. Ten conforming implementations.
Identifold is a stable, language-neutral identity contract for applications that need a UUIDv7 for storage, a typed public identifier for software, and a short checksummed reference for people.
MID 019d4c72-c910-7a84-b313-53c3ac61a32f
PID order_01kn675j8gfa2b64tkrep638sf
REF ORD-7K4M-2P8Q-3D-9
Application identifiers rarely have only one audience:
- databases need a compact, canonical key with predictable ordering;
- APIs and logs benefit from identifiers that carry their resource type; and
- people need references that are short enough to read, copy, and verify.
Using one format for all three jobs usually compromises at least one of them. Exposing raw UUIDs loses useful type context. Storing prefixed strings as primary keys couples presentation to persistence. Truncating identifiers for people creates collision and transcription risks.
Identifold exists to keep those responsibilities separate without creating three unrelated identities:
MID <-> PID
REF -> storage -> MID
The MID and PID are deterministic representations of the same UUIDv7. A REF is independently allocated, checksummed, and resolved through application storage. That distinction makes uniqueness ownership explicit and avoids pretending a short human reference can be reversed without state.
The project also solves a cross-language problem: identifier rules tend to drift when every service reimplements conversion, normalization, checksums, and error handling. Identifold freezes one wire contract, one conformance corpus, and one error taxonomy, then verifies every implementation against the same public vectors.
Suppose Bob disputes a charge for an employee order. His receipt shows ORD-7K4M-2P8Q-3D-9, so that is the value he reads over the phone. Alice, working in HR operations, enters the same REF into the internal support tool.
The identifiers have different jobs as the request crosses the system:
- Bob and Alice use the REF. It is short, grouped for reading, and includes a check symbol that catches common transcription mistakes.
- The application normalizes and validates the REF. Passing the checksum proves only that the text is well formed; it does not prove that the order exists.
- Storage resolves the REF to its MID. A unique lookup such as
(namespace, reference) -> machine_idreturns019d4c72-c910-7a84-b313-53c3ac61a32f. - Services use the PID at public boundaries. An order API, event, or log can expose
order_01kn675j8gfa2b64tkrep638sf, preserving both the resource type and the same 128 UUID bits. - The receiving service converts the PID back to the MID. It then queries the canonical order row by UUID rather than storing a second identity.
Bob and Alice REF mapping Canonical order
ORD-7K4M-...-9 -> validate -> lookup ---------------------> 019d4c72-...-a32f
REF REF -> MID MID
|
+-> derive order_01kn...638sf for an API or log
PID <-> MID
The REF does not mathematically turn into a PID. The stored REF mapping resolves directly to the MID; a PID can then be derived from that MID when a typed public representation is useful. This keeps human usability, public type context, and database identity separate while still referring to one order.
None of these values authorizes Alice to view the order. Authentication establishes who Alice is, and application policy decides whether she may access Bob's dispute.
Every supported implementation is conformant, published, and publicly installable at version 1.0.0.
| Language | Distribution | Install coordinate | Release |
|---|---|---|---|
| TypeScript / JavaScript | npm | @greyfoundry/identifold |
Live |
| Python | PyPI | identifold |
Live |
| Java | Maven Central | io.github.greyfoundry:identifold |
Live |
| C# | NuGet | Greyfoundry.Identifold |
Live |
| Go | Go package index | github.com/greyfoundry/identifold/packages/go |
Live |
| PHP | Packagist | greyfoundry/identifold |
Live |
| Kotlin | Maven Central | io.github.greyfoundry:identifold-kotlin |
Live |
| Rust | crates.io | identifold |
Live |
| Ruby | RubyGems | identifold |
Live |
| Swift | Swift Package Manager | https://github.com/greyfoundry/identifold.git |
Live |
See IMPLEMENTATIONS.md for package directories, runtime requirements, and verification coverage.
| Language | Command or package declaration |
|---|---|
| TypeScript / JavaScript | npm install @greyfoundry/identifold or pnpm add @greyfoundry/identifold |
| Python | python -m pip install identifold |
| Java | mvn dependency:get -Dartifact=io.github.greyfoundry:identifold:1.0.0 |
| C# | dotnet add package Greyfoundry.Identifold --version 1.0.0 |
| Go | go get github.com/greyfoundry/identifold/packages/go@v1.0.0 |
| PHP | composer require greyfoundry/identifold:^1.0 |
| Kotlin | mvn dependency:get -Dartifact=io.github.greyfoundry:identifold-kotlin:1.0.0 |
| Rust | cargo add identifold@1.0.0 |
| Ruby | gem install identifold -v 1.0.0 |
| Swift | Add https://github.com/greyfoundry/identifold.git from version 1.0.0 in SwiftPM |
Complete Maven, Gradle, and Swift Package Manager declarations are available in the installation guide.
Each example converts the same UUIDv7 MID into an order_ PID and parses it back to the original MID. The checked-in programs under examples/ execute this round trip in CI.
TypeScript / JavaScript
import { parsePublicId, publicIdFromMachineId } from "@greyfoundry/identifold";
const mid = "019d4c72-c910-7a84-b313-53c3ac61a32f";
const pid = publicIdFromMachineId(mid, "order");
const parsed = parsePublicId(pid, "order");Python
from identifold import parse_public_id, public_id_from_machine_id
mid = "019d4c72-c910-7a84-b313-53c3ac61a32f"
pid = public_id_from_machine_id(mid, "order")
parsed = parse_public_id(pid, "order")Java
import io.greyfoundry.identifold.Identifold;
var mid = "019d4c72-c910-7a84-b313-53c3ac61a32f";
var pid = Identifold.publicIdFromMachineId(mid, "order");
var parsed = Identifold.parsePublicId(pid);C#
using Greyfoundry.Identifold;
var mid = "019d4c72-c910-7a84-b313-53c3ac61a32f";
var pid = Identifiers.PublicIdFromMachineId(mid, "order");
var parsed = Identifiers.ParsePublicId(pid);Go
import "github.com/greyfoundry/identifold/packages/go"
mid := "019d4c72-c910-7a84-b313-53c3ac61a32f"
pid, err := identifold.PublicIDFromMachineID(mid, "order")
parsed, err := identifold.ParsePublicID(pid)PHP
use Greyfoundry\Identifold\Identifold;
$mid = '019d4c72-c910-7a84-b313-53c3ac61a32f';
$pid = Identifold::publicIdFromMachineId($mid, 'order');
$parsed = Identifold::parsePublicId($pid);Kotlin
import io.greyfoundry.identifold.KotlinIdentifold
val mid = "019d4c72-c910-7a84-b313-53c3ac61a32f"
val pid = KotlinIdentifold.publicIdFromMachineId(mid, "order")
val parsed = KotlinIdentifold.parsePublicId(pid)Rust
let mid = "019d4c72-c910-7a84-b313-53c3ac61a32f";
let pid = identifold::public_id_from_machine_id(mid, "order")?;
let parsed = identifold::parse_public_id(&pid)?;Ruby
require "identifold"
mid = "019d4c72-c910-7a84-b313-53c3ac61a32f"
pid = Identifold.public_id_from_machine_id(mid, "order")
parsed = Identifold.parse_public_id(pid)Swift
import Identifold
let mid = "019d4c72-c910-7a84-b313-53c3ac61a32f"
let pid = try Identifiers.publicID(from: mid, namespace: "order")
let parsed = try Identifiers.parsePublicID(pid)TypeScript and Python additionally provide UUIDv7 creation conveniences. REF allocation requires application storage, so production REF examples belong at the storage boundary rather than in a stateless conversion snippet.
- Store the MID as the canonical identity, preferably in a native UUID or 16-byte database type.
- Derive the PID from the MID and registered public prefix unless an application has a specific indexing need.
- Store each REF as a separate unique value mapped to its MID and namespace.
- Implement random
ReferenceStore.reserveas one atomic insert-or-conflict operation backed by a unique constraint. - Implement
SequenceAllocator.allocateso advancing a scoped sequence and binding it to a MID happen in the same transaction. - Preserve retired namespace definitions for historical parsing.
An in-memory uniqueness check is not a production allocation boundary for multiple processes. Calendar-year sequence scopes use the UTC year.
The protected main branch requires 17 hosted checks covering:
- Node.js 22, 24, and 26;
- Python 3.12, 3.13, and 3.14;
- Go, Rust, Java, .NET, PHP, Ruby, Kotlin, and Swift;
- PostgreSQL 18 concurrency and allocation behavior; and
- CodeQL analysis for JavaScript, TypeScript, and Python.
Every language runs the same deterministic vectors through the conformance runner. Package-specific builds, examples, formatting, type resolution, and publication checks run alongside that shared contract.
- Complete wiki handbook
- Stable specification
- Implementation matrix
- Compatibility policy
- Production examples
- Conformance runner
- PostgreSQL integration
- Public roadmap
- Security policy
Identifiers identify. They do not authenticate callers or authorize access. Knowledge of an identifier or reference must never grant access by itself.
- UUIDv7 follows RFC 9562.
- Public IDs follow TypeID specification v0.3.
- Random reference payloads use the Crockford Base32 data alphabet and modulo-37 check-symbol convention.
Apache-2.0. See LICENSE.