npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

global-identifier

v2.1.1

Published

Type-safe, compact identifiers with optional encryption support

Readme

global-identifier

Type-safe, compact, and cryptographically secure identifiers for distributed systems.

TypeScript License: MIT Node.js Browser Compatible


Why global-identifier?

Modern distributed systems need identifiers that are:

  • Self-describing – Know what type of entity an ID refers to at a glance
  • Compact – Efficient for URLs, databases, and wire transfer
  • Type-safe – Prevent mixing up User IDs with Order IDs at compile time
  • Secure – Encrypt sensitive data, sign for authenticity
  • Sortable – Chronologically ordered for time-series data

global-identifier delivers all of this in a zero-dependency*, developer-friendly package.

*CBOR encoding uses the cbor-x package for optimal performance.


Table of Contents


Installation

npm install global-identifier

Optional peer dependencies:

# For Zod runtime validation
npm install zod

Quick Start

import { Identifier, createCodec } from "global-identifier";

// 1. Define your type mapping
const codec = createCodec({
  types: {
    User: "usr",
    Order: "ord",
    Product: "prd"
  }
});

// 2. Create identifiers
const userId = new Identifier("User", { id: "abc-123", name: "Alice" });
const orderId = new Identifier("Order", "order-456");

// 3. Encode to compact strings
const encodedUser = codec.encode(userId);
// → "usr_o2JpZGdhYmMtMTIzZG5hbWVlQWxpY2U"

const encodedOrder = codec.encode(orderId);
// → "ord_aW9yZGVyLTQ1Ng"

// 4. Decode back to typed identifiers
const decoded = codec.decode<"User", { id: string; name: string }>(encodedUser);
console.log(decoded.type); // "User"
console.log(decoded.data); // { id: "abc-123", name: "Alice" }

Core Concepts

Identifier

The Identifier class is an immutable container holding:

  • type: A string identifying what kind of entity this is
  • data: Any serializable payload (string, number, object, array)
// String data
const userId = new Identifier("User", "user-123");

// Object data
const orderId = new Identifier("Order", {
  orderId: "ord-456",
  customerId: "cust-789",
  items: ["item-1", "item-2"]
});

// Immutability
userId.data = "new-value"; // ❌ Error: Cannot assign to read-only property

Key characteristics:

| Property | Description | | ------------ | -------------------------------------------------- | | Immutable | Instance is frozen after creation | | Generic | Identifier<TType, TData> for full type inference | | Serializable | toJSON() / fromJSON() for easy persistence | | Comparable | equals() method with deep equality |

Codec

A Codec handles encoding Identifiers to compact strings and decoding them back:

const codec = createCodec({
  types: { User: "usr", Order: "ord" }
});

// Encoding: Identifier → String
const encoded = codec.encode(new Identifier("User", "data"));

// Decoding: String → Identifier
const decoded = codec.decode(encoded);

Encoding pipeline:

Identifier { type, data }
    ↓
CBOR encode (data → binary)
    ↓
Base64URL encode (binary → URL-safe string)
    ↓
Prefix/suffix type indicator
    ↓
"usr_b2JqZWN0LWRhdGE"

Type Mapping

The type mapping is a simple object that associates:

  • Type names (what you use in code): 'User', 'Order', 'Product'
  • Short prefixes (what appears in encoded strings): 'usr', 'ord', 'prd'
const types = {
  User: "usr", // 3 chars
  Organization: "org", // Shortened from 12 chars
  Transaction: "txn" // Domain-specific abbreviation
} as const;

Why mappings?

  • Shorter IDs: usr_abc vs User_abc
  • Obfuscation: Don't expose internal type names
  • Flexibility: Change internal names without breaking encoded IDs

Features

Configurable Encoding

Default Encoding

const codec = createCodec({
  types: { User: "usr" }
});

codec.encode(new Identifier("User", "data"));
// → "usr_ZGF0YQ"

Custom Separator

// Colon separator
const codec = createCodec({
  types: { User: "usr" },
  separator: ":"
});
codec.encode(new Identifier("User", "data"));
// → "usr:ZGF0YQ"

// Dash separator
const codec = createCodec({
  types: { User: "usr" },
  separator: "-"
});
codec.encode(new Identifier("User", "data"));
// → "usr-ZGF0YQ"

// No separator
const codec = createCodec({
  types: { User: "usr" },
  separator: ""
});
codec.encode(new Identifier("User", "data"));
// → "usrZGF0YQ"

Type Position

// Prefix (default) - type at start
const prefixCodec = createCodec({
  types: { User: "usr" },
  typePosition: "prefix"
});
// → "usr_ZGF0YQ"

// Suffix - type at end
const suffixCodec = createCodec({
  types: { User: "usr" },
  typePosition: "suffix"
});
// → "ZGF0YQ_usr"

Sortable IDs

Enable lexicographically sortable IDs with embedded timestamps:

const codec = createCodec({
  types: { Event: "evt" },
  sortable: { enabled: true }
});

// IDs include a Crockford Base32 timestamp
const id1 = codec.encode(new Identifier("Event", "first"));
// → "evt_01JFQK4X20ZGlyc3Q"

// Later ID sorts after earlier ID
const id2 = codec.encode(new Identifier("Event", "second"));
// → "evt_01JFQK5Y30c2Vjb25k"

// Lexicographic sorting = chronological sorting!
[id2, id1].sort(); // → [id1, id2]

Sortable configuration:

const codec = createCodec({
  types: { Event: "evt" },
  sortable: {
    enabled: true,
    precision: "millisecond", // or 'second'
    epoch: new Date("2024-01-01"), // Custom epoch for shorter IDs
    length: 10 // Timestamp characters (10 = ~35 years)
  },
  typePosition: "suffix" // Better for cross-type sorting
});

Pro tip: Use typePosition: 'suffix' with sortable IDs so the timestamp is at the start, enabling sorting across different entity types.

Codec Extension & Multi-Format Support

Extending a Codec

const userCodec = createCodec({
  types: { User: "usr" },
  separator: ":"
});

// Extend with additional types
const extendedCodec = userCodec.extend({
  types: { Order: "ord", Product: "prd" }
});

// Extended codec handles all types
extendedCodec.encode(new Identifier("User", "data")); // ✅
extendedCodec.encode(new Identifier("Order", "data")); // ✅
extendedCodec.encode(new Identifier("Product", "data")); // ✅

Merging Codecs

import { mergeCodecs } from "global-identifier";

const codec1 = createCodec({ types: { User: "usr" } });
const codec2 = createCodec({ types: { Order: "ord" } });
const codec3 = createCodec({ types: { Product: "prd" } });

const merged = mergeCodecs(codec1, codec2, codec3);

Multi-Format Codec (Migration Support)

When migrating from one encoding format to another:

import { createMultiCodec } from "global-identifier";

// Old format (legacy)
const legacyCodec = createCodec({
  types: { User: "usr" },
  separator: ":"
});

// New format
const newCodec = createCodec({
  types: { User: "usr", Order: "ord" },
  separator: "_"
});

// Multi-codec decodes BOTH formats
const multi = createMultiCodec(newCodec, legacyCodec);

multi.decode("usr:bGVnYWN5"); // ✅ Decodes legacy format
multi.decode("usr_bmV3"); // ✅ Decodes new format
multi.encode(id); // Uses new format (primary codec)

Symmetric Encryption

Encrypt identifiers with AES-256-GCM using password-based key derivation:

import { Identifier, encrypt, decrypt } from "global-identifier";

// Create identifier with sensitive data
const sensitiveId = new Identifier("User", {
  ssn: "123-45-6789",
  email: "[email protected]"
});

// Encrypt with password
const encrypted = await encrypt(sensitiveId, {
  password: "strong-password-here"
});

// Decrypt
const decrypted = await decrypt(encrypted, {
  password: "strong-password-here"
});

console.log(decrypted.data.ssn); // '123-45-6789'

With pre-generated key:

import { generateKey, encrypt, decrypt } from "global-identifier";

// Generate a secure key
const key = await generateKey();

// Encrypt/decrypt with key (faster, no key derivation)
const encrypted = await encrypt(id, { key });
const decrypted = await decrypt(encrypted, { key });

Serialization for storage/transfer:

import { serializeEncrypted, deserializeEncrypted } from "global-identifier";

// Serialize to Base64URL string
const serialized = serializeEncrypted(encrypted);
// → "eyJjIjoiYWJj..."

// Deserialize back
const deserialized = deserializeEncrypted(serialized);
const decrypted = await decrypt(deserialized, { password });

Asymmetric Encryption

Use ECDH for secure key exchange and encryption:

import { Identifier, generateEncryptionKeyPair, encryptAsymmetric, decryptAsymmetric, exportKeyPair } from "global-identifier";

// Generate key pair (keep privateKey secret!)
const keyPair = await generateEncryptionKeyPair("P-256");

// Export for storage/distribution
const exported = await exportKeyPair(keyPair, "encryption");
// Share exported.publicKey, keep exported.privateKey secret

// Encrypt with public key
const encrypted = await encryptAsymmetric(new Identifier("User", "secret-data"), keyPair.publicKey);

// Decrypt with private key
const decrypted = await decryptAsymmetric(encrypted, keyPair.privateKey);

Supported curves:

| Curve | Security | Performance | | ----- | -------- | ----------- | | P-256 | 128-bit | Fastest | | P-384 | 192-bit | Balanced | | P-521 | 256-bit | Most secure |

Digital Signatures

Sign identifiers for authenticity verification (JWT-like):

import { Identifier, generateSigningKeyPair, sign, verify } from "global-identifier";

// Generate signing key pair
const keyPair = await generateSigningKeyPair("P-256");

// Sign an identifier
const signed = await sign(new Identifier("Transaction", { amount: 1000, currency: "USD" }), keyPair.privateKey, {
  kid: "key-2024-01", // Key ID for rotation
  iss: "payment-service", // Issuer
  expiresIn: 3600 // 1 hour expiration
});

// Verify signature
const result = await verify(signed, keyPair.publicKey);

if (result.valid) {
  console.log("Verified:", result.id.data);
} else {
  console.error("Invalid:", result.error);
  if (result.expired) {
    console.error("Signature has expired");
  }
}

Signed identifier structure:

{
  id: Identifier,           // The signed identifier
  signature: string,        // ECDSA signature (Base64URL)
  kid: string,              // Key ID
  alg: string,              // Algorithm (ES256, ES384, ES512)
  iat: string,              // Issued at (ISO 8601)
  iss?: string,             // Issuer
  exp?: string,             // Expiration (ISO 8601)
}

JWKS Key Management

Enterprise-grade key management with JSON Web Key Sets:

import { createKeyStore, KeyStore } from "global-identifier";

// Create a key store with signing and encryption keys
const keyStore = await createKeyStore({
  signingKeys: 2, // Generate 2 signing keys (for rotation)
  encryptionKeys: 1, // Generate 1 encryption key
  defaultCurve: "P-256"
});

// Get JWKS for public distribution
const jwks = keyStore.getJWKS();
// Publish at: https://your-service.com/.well-known/jwks.json

// Get active keys for operations
const signingKey = keyStore.getActiveSigningKey();
const encryptionKey = keyStore.getActiveEncryptionKey();

// Sign with key from store
const signed = await sign(id, signingKey.privateKey, {
  kid: signingKey.kid,
  iss: "your-service"
});

Fetching keys from remote JWKS:

import { createJWKSFetcher } from "global-identifier";

const fetcher = createJWKSFetcher({
  issuer: "https://auth.example.com",
  jwksUri: "https://auth.example.com/.well-known/jwks.json",
  cacheDuration: 3600000 // Cache for 1 hour
});

// Fetch and cache public keys
const keys = await fetcher.fetchKeys();

// Find key by ID
const key = await fetcher.getKey("key-2024-01");
if (key) {
  const result = await verify(signed, key);
}

Zod Validation

Runtime validation with Zod schemas:

import { z } from "zod";
import { createValidated, validate } from "global-identifier/zod";

// Define schema
const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  age: z.number().min(0).max(150)
});

// Create validated identifier
const userId = createValidated("User", UserSchema, {
  id: "550e8400-e29b-41d4-a716-446655440000",
  email: "[email protected]",
  age: 30
});
// ✅ Type-safe: userId.data is typed as z.infer<typeof UserSchema>

// Validation error
createValidated("User", UserSchema, {
  id: "not-a-uuid",
  email: "invalid-email",
  age: -5
});
// ❌ Throws ValidationError with Zod issues

// Validate existing identifier
const validated = validate(existingId, UserSchema);

Advanced Usage

Custom Separators

Choose separators based on your use case:

| Separator | Example | Best For | | ------------- | ------------ | --------------------- | | _ (default) | usr_abc123 | General purpose | | : | usr:abc123 | URN-style identifiers | | - | usr-abc123 | URL slugs | | . | usr.abc123 | Domain-like | | `` (none) | usrabc123 | Maximum compactness |

Type Position (Prefix vs Suffix)

Prefix (default):

usr_ZGF0YQ
^^^
Type first - good for filtering by type

Suffix:

ZGF0YQ_usr
      ^^^
Type last - better with sortable timestamps

Branded Types

Prevent mixing up different ID types at compile time:

import { Identifier, type BrandedIdentifier } from "global-identifier";

// Define branded types
type UserId = BrandedIdentifier<"User", string, "UserId">;
type OrderId = BrandedIdentifier<"Order", string, "OrderId">;

// Create branded IDs
const userId = new Identifier("User", "user-123") as UserId;
const orderId = new Identifier("Order", "order-456") as OrderId;

// Type-safe functions
function processUser(id: UserId) {
  /* ... */
}
function processOrder(id: OrderId) {
  /* ... */
}

processUser(userId); // ✅ OK
processUser(orderId); // ❌ Type error!

Migration Between Formats

Safely migrate from one encoding format to another:

import { createCodec, createMultiCodec } from "global-identifier";

// Phase 1: Read both, write new
const legacyCodec = createCodec({
  types: { User: "usr" },
  separator: ":"
});

const newCodec = createCodec({
  types: { User: "usr" },
  separator: "_"
});

const multiCodec = createMultiCodec(newCodec, legacyCodec);

// Read from any format
function getUser(id: string) {
  const decoded = multiCodec.decode(id); // Works with both formats
  return fetchUser(decoded.data);
}

// Write in new format
function createUser(data: UserData) {
  const id = new Identifier("User", data);
  return multiCodec.encode(id); // Always uses new format
}

// Phase 2: Once all IDs migrated, remove legacy codec
const finalCodec = newCodec;

API Reference

Core

| Export | Description | | -------------------- | ----------------------------------------------- | | Identifier | Immutable identifier class | | GlobalId | Alias for Identifier (backward compatibility) | | createCodec() | Create a new codec | | mergeCodecs() | Merge multiple codecs | | createMultiCodec() | Create multi-format decoder |

Encoding

| Export | Description | | ------------------- | ------------------------- | | encodeBase64Url() | Encode bytes to Base64URL | | decodeBase64Url() | Decode Base64URL to bytes | | encodeCbor() | Encode value to CBOR | | decodeCbor() | Decode CBOR to value |

Symmetric Crypto

| Export | Description | | ------------------------ | -------------------------------- | | encrypt() | Encrypt identifier (AES-256-GCM) | | decrypt() | Decrypt identifier | | generateKey() | Generate AES key | | deriveKey() | Derive key from password | | serializeEncrypted() | Serialize encrypted data | | deserializeEncrypted() | Deserialize encrypted data |

Asymmetric Crypto

| Export | Description | | ----------------------------- | --------------------------- | | generateEncryptionKeyPair() | Generate ECDH key pair | | generateSigningKeyPair() | Generate ECDSA key pair | | encryptAsymmetric() | Encrypt with public key | | decryptAsymmetric() | Decrypt with private key | | sign() | Sign identifier | | verify() | Verify signature | | exportKeyPair() | Export keys to JWK | | importPublicKey() | Import public key from JWK | | importPrivateKey() | Import private key from JWK |

JWKS

| Export | Description | | --------------------- | -------------------------- | | KeyStore | In-memory key store | | createKeyStore() | Create key store with keys | | JWKSFetcher | Remote JWKS fetcher | | createJWKSFetcher() | Create JWKS fetcher |

Errors

| Export | Description | | -------------------- | ----------------------- | | IdentifierError | Base error class | | EncodingError | Encoding failures | | DecodingError | Decoding failures | | UnknownTypeError | Unknown type name | | UnknownPrefixError | Unknown type prefix | | EncryptionError | Encryption failures | | DecryptionError | Decryption failures | | KeyDerivationError | Key derivation failures | | ValidationError | Validation failures |


Performance

Benchmarks on Node.js 22, Apple M1 Pro:

| Operation | Speed | Notes | | ------------------ | ------------- | -------------- | | Encode (simple) | ~950K ops/sec | String data | | Encode (complex) | ~520K ops/sec | Nested objects | | Decode (simple) | ~1.2M ops/sec | String data | | Decode (complex) | ~680K ops/sec | Nested objects | | Symmetric encrypt | ~15K ops/sec | AES-256-GCM | | Symmetric decrypt | ~18K ops/sec | AES-256-GCM | | Asymmetric encrypt | ~2K ops/sec | ECDH + AES | | Sign | ~5K ops/sec | ECDSA P-256 | | Verify | ~3K ops/sec | ECDSA P-256 |

Size comparison:

| Format | 'user-123' | { id: 'abc', name: 'Alice' } | | ----------------- | ---------- | ------------------------------ | | JSON | 31 chars | 53 chars | | global-identifier | 17 chars | 33 chars | | Savings | 45% | 38% |


Security

Algorithms

| Purpose | Algorithm | Details | | -------------------- | ------------- | -------------------- | | Symmetric encryption | AES-256-GCM | 128-bit auth tag | | Key derivation | PBKDF2-SHA256 | 100K iterations | | Key exchange | ECDH | P-256, P-384, P-521 | | Signing | ECDSA | P-256, P-384, P-521 | | Encoding | Base64URL | URL-safe, no padding |

Best Practices

  1. Passwords: Use strong passwords (12+ characters, mixed case, numbers, symbols)
  2. Keys: Store private keys in secure vaults (AWS KMS, Google Secret Manager, HashiCorp Vault)
  3. Rotation: Rotate signing keys periodically (use kid for key identification)
  4. Expiration: Set reasonable expiration times for signed identifiers
  5. HTTPS: Always transmit encrypted/signed data over HTTPS

What global-identifier Does NOT Do

  • ❌ Store keys (use a secure vault)
  • ❌ Manage user passwords (use a proper auth system)
  • ❌ Provide authentication (use with an auth layer)
  • ❌ Replace UUIDs for primary keys (use alongside them)

Best Practices

Choosing Type Prefixes

// ✅ Good: Short, meaningful, unique
const types = {
  User: "usr",
  Organization: "org",
  Transaction: "txn",
  Invoice: "inv"
};

// ❌ Bad: Too long, confusing, colliding
const types = {
  User: "user", // Too long
  UserAccount: "usr", // Collision with User
  Transaction: "t" // Too short, unclear
};

When to Use Each Feature

| Use Case | Feature | | --------------------------- | --------------------- | | Public IDs in URLs | Basic encoding | | Time-ordered events | Sortable IDs | | Sensitive data | Symmetric encryption | | Cross-service communication | Asymmetric encryption | | API authentication | Digital signatures | | Microservices | JWKS key management | | Form validation | Zod integration |

Error Handling

import { codec, isDecodingError, isEncryptionError } from "global-identifier";

try {
  const id = codec.decode(userInput);
} catch (error) {
  if (isDecodingError(error)) {
    // Invalid format, unknown prefix, etc.
    console.error("Invalid ID format:", error.message);
  }
  throw error;
}

// Or use safe decode
const result = codec.decodeSafe(userInput);
if (result.success) {
  console.log(result.data);
} else {
  console.error(result.error);
}

FAQ

Why not just use UUIDs?

UUIDs are great for unique identifiers but lack:

  • Type information (what entity is this?)
  • Payload capacity (just a random string)
  • Sortability (random order)
  • Encryption (plaintext only)

Use global-identifier when you need these features. Use UUIDs as the data payload for uniqueness.

Can I use this in the browser?

Yes! All crypto operations use the Web Crypto API, which is available in all modern browsers.

How do I handle key rotation?

Use the JWKS features:

  1. Generate new key, keep old key
  2. Sign new tokens with new key
  3. Verify with both keys (by kid)
  4. After grace period, remove old key

What's the maximum data size?

Practical limit is a few KB. For larger payloads, store the data elsewhere and use global-identifier for the reference.

Is it compatible with existing systems?

Yes! Use createMultiCodec to decode legacy formats while encoding in your preferred format.


Contributing

Contributions are welcome! Please see our Contributing Guide.

# Clone the repository
git clone https://github.com/anthropics/global-identifier.git

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

License

MIT License - see LICENSE for details.