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

opaqid

v1.0.0

Published

Stateless, reversible integer obfuscation - Convert sequential IDs to URL-safe strings without database storage

Readme

Opaqid

Stateless, reversible integer obfuscation - Convert sequential IDs to URL-safe strings without database storage.

const opaqid = new Opaqid('your-secret-salt');

opaqid.encode(1);      // "f4GZ8zzYZaW"
opaqid.encode(2);      // "DJb7eUnfdqd"  
opaqid.encode(3);      // "mpC9L9p6zBw"

opaqid.decode("f4GZ8zzYZaW");  // 1n (BigInt)
opaqid.decodeNumber("f4GZ8zzYZaW");  // 1

Why Opaqid?

| Problem | Solution | |---------|----------| | Sequential IDs expose business info | → Obfuscated, random-looking output | | UUID requires DB storage | → Stateless, no storage needed | | Hashids/Sqids are pattern-analyzable | → Feistel cipher resists cryptanalysis |

Installation

npm install opaqid

Usage

Basic

import Opaqid from 'opaqid';

const opaqid = new Opaqid('your-secret-salt');

// Encode
const encoded = opaqid.encode(12345);  // "5fI79m3L2nJ"

// Decode
const decoded = opaqid.decodeNumber(encoded);  // 12345

With Options

const opaqid = new Opaqid('your-secret-salt', {
  minLength: 12,    // Minimum output length (default: 8)
  rounds: 6,        // Feistel rounds (default: 4, min: 2)
  alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',  // Custom alphabet
});

BigInt Support

const bigNum = BigInt('9007199254740993');  // > Number.MAX_SAFE_INTEGER

const encoded = opaqid.encode(bigNum);
const decoded = opaqid.decode(encoded);  // Returns BigInt

console.log(decoded === bigNum);  // true

API

new Opaqid(salt, options?)

Creates a new Opaqid instance.

| Parameter | Type | Description | |-----------|------|-------------| | salt | string | Secret salt (min 4 chars). Different salts produce different outputs. | | options.minLength | number | Minimum output length (default: 8) | | options.rounds | number | Feistel cipher rounds (default: 4, min: 2). More = slower but stronger. | | options.alphabet | string | Custom alphabet (min 16 unique chars, default: Base62) |

encode(id: number | bigint): string

Encodes an integer to an obfuscated string.

decode(encoded: string): bigint

Decodes an obfuscated string back to a BigInt.

decodeNumber(encoded: string): number

Decodes to a number. Throws if result exceeds Number.MAX_SAFE_INTEGER.

isValid(encoded: string): boolean

Checks if a string contains only valid alphabet characters.

How It Works

Opaqid uses a Feistel cipher combined with Base62 encoding:

int → 64-bit bytes → Feistel Encrypt → Base62 Encode → string

Why Feistel?

  1. Guaranteed reversibility - Same algorithm for encryption/decryption
  2. Round function doesn't need to be reversible - Implementation flexibility
  3. Proven security - DES, Blowfish, Twofish all use Feistel networks
  4. 4+ rounds = PRP - Luby-Rackoff theorem guarantees pseudorandom permutation

Security Properties

  • Different salts → completely different outputs
  • Sequential inputs → no visible pattern
  • Wrong salt → wrong decode (garbage, not error)
  • No cryptographic dependencies - Pure JavaScript

Performance

Encode: ~500,000 ops/sec
Decode: ~250,000 ops/sec

Comparison

| Feature | Hashids | Sqids | Opaqid | |---------|---------|-------|-----------| | Reversible | ✓ | ✓ | ✓ | | Salt/Key | ✓ | alphabet shuffle | ✓ (Feistel keys) | | minLength | ✓ | ✓ | ✓ | | Pattern resistant | ✗ | ✗ | | | BigInt | partial | partial | ✓ | | Crypto-grade | ✗ | ✗ | |

Security Note

Opaqid provides obfuscation, not encryption. While it's much stronger than Hashids/Sqids:

  • ✅ Use for: Hiding sequential IDs, preventing enumeration
  • ❌ Don't use for: Storing secrets, authentication tokens

Always implement proper access control—obfuscated IDs are not a replacement for authorization.

Documentation

License

MIT