Skip to content

Latest commit

Β 

History

46 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Identifold

CI Languages Python PostgreSQL CodeQL GitHub release License

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

🎯 Motivation

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.

A disputed-charge walkthrough

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:

  1. Bob and Alice use the REF. It is short, grouped for reading, and includes a check symbol that catches common transcription mistakes.
  2. 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.
  3. Storage resolves the REF to its MID. A unique lookup such as (namespace, reference) -> machine_id returns 019d4c72-c910-7a84-b313-53c3ac61a32f.
  4. 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.
  5. 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.

πŸ“¦ 1.0 release status

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.

πŸš€ Install

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.

Ten-language quick starts

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.

Design boundaries

  • 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.reserve as one atomic insert-or-conflict operation backed by a unique constraint.
  • Implement SequenceAllocator.allocate so 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.

What is verified

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.

Documentation

Security boundary

Identifiers identify. They do not authenticate callers or authorize access. Knowledge of an identifier or reference must never grant access by itself.

Standards

  • 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.

License

Apache-2.0. See LICENSE.

About

Portable UUIDv7, TypeID, and checksummed human references with conforming implementations for ten languages.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages