Stateless, reversible integer obfuscation - Convert sequential IDs to URL-safe strings without database storage
npm install opaqidStateless, reversible integer obfuscation - Convert sequential IDs to URL-safe strings without database storage.
``typescript
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
`
| 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 |
`bash`
npm install opaqid
`typescript
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
`
`typescript`
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
});
`typescript
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
`
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) |
Encodes an integer to an obfuscated string.
Decodes an obfuscated string back to a BigInt.
Decodes to a number. Throws if result exceeds Number.MAX_SAFE_INTEGER.
Checks if a string contains only valid alphabet characters.
Opaqid uses a Feistel cipher combined with Base62 encoding:
``
int → 64-bit bytes → Feistel Encrypt → Base62 Encode → string
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
- ✅ Different salts → completely different outputs
- ✅ Sequential inputs → no visible pattern
- ✅ Wrong salt → wrong decode (garbage, not error)
- ✅ No cryptographic dependencies - Pure JavaScript
```
Encode: ~500,000 ops/sec
Decode: ~250,000 ops/sec
| Feature | Hashids | Sqids | Opaqid |
|---------|---------|-------|-----------|
| Reversible | ✓ | ✓ | ✓ |
| Salt/Key | ✓ | alphabet shuffle | ✓ (Feistel keys) |
| minLength | ✓ | ✓ | ✓ |
| Pattern resistant | ✗ | ✗ | ✓ |
| BigInt | partial | partial | ✓ |
| Crypto-grade | ✗ | ✗ | ✓ |
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.
- Algorithm Design (English)
- 알고리즘 설계 (한국어)
- README (한국어)
MIT