DEV Community

Vanni Fan
Vanni Fan

Posted on

idmix: A Better Alternative to Sqids for Structured Access Keys

idmix: A Better Alternative to Sqids for Structured Access Keys

If you've ever needed to generate short, URL-safe identifiers from integers, you've probably encountered Sqids (formerly Hashids). While Sqids is popular and works well for basic use cases, it has significant limitations when building structured access keys or API tokens.

Today I want to introduce idmix - a short ID encoding library that addresses these limitations while adding powerful new features.

The Problem with Current Solutions

Let's say you're building an API key system. You want to encode:

  • User ID (uint64)
  • Expiration timestamp (uint32)
  • Permission flags (uint16)

With Sqids, you'd do something like:

import { encode } from 'sqids';
const key = encode([userId, expiry, perms]);
Enter fullscreen mode Exit fullscreen mode

But there are problems:

  1. No negative numbers - Sqids only supports non-negative integers
  2. No type information - When decoding, you get [12345, 1690000000, 7] but don't know which is which
  3. Deterministic output - Same input always produces the same string, making keys vulnerable to scanning
  4. Language inconsistencies - Different language implementations may behave slightly differently

Enter idmix

idmix solves all these problems:

import idmix "github.com/Vanni-Fan/idmix/golang"

m, _ := idmix.New()

// Encode with types preserved
key, _ := m.Encode(uint64(userId), uint32(expiry), uint16(perms))
// → "9XqK2mPpL"

// Decode - types automatically restored
values, _ := m.Decode(key)
uid := values[0].(uint64)     // 12345
exp := values[1].(uint32)     // 1690000000
perms := values[2].(uint16)   // 7
Enter fullscreen mode Exit fullscreen mode

Key Features

1. Type-Aware Encoding

Every value carries its original type information. No external schema needed:

from idmix import IdMix, u16, i64, u32

m = IdMix.new()
key = m.encode(u16(5), i64(-1), u32(40))

values = m.decode(key)
# values[0] is uint16: 5
# values[1] is int64: -1
# values[2] is uint32: 40
Enter fullscreen mode Exit fullscreen mode

2. Nine-Language Interoperability

idmix provides consistent implementations across:

  • Go (reference implementation)
  • Rust
  • Python
  • JavaScript/TypeScript
  • Java
  • C#
  • PHP
  • C++
  • C

Encode in your Go backend, decode in your Python data pipeline or JavaScript frontend - guaranteed compatibility.

3. 32-State Polymorphic Encoding

This is my favorite feature. Same data, different outputs:

m, _ := idmix.New()

key1, _ := m.Encode(uint64(12345))  // "a3K9mP"
key2, _ := m.Encode(uint64(12345))  // "x7Q2nR"
key3, _ := m.Encode(uint64(12345))  // "b5M8vW"

// All decode to the same value
Enter fullscreen mode Exit fullscreen mode

This provides natural obfuscation against brute-force scanning attacks. An attacker can't predict what your keys look like, even if they know the underlying data.

4. Lightweight Self-Validation

Built-in 2-bit XOR checksum catches approximately 75% of random tampering attempts without any additional storage.

5. Performance

idmix is fast. Really fast:

Operation vs Sqids (Go) vs Sqids (Rust)
Encode [1,2,3] 47× faster 12× faster
Encode AccessKey 21× faster 16× faster
Decode AccessKey 2.4× faster 4.2× faster

Comparison Table

Feature Sqids idmix
Input types Non-negative integers only Any integer + short strings
Negative numbers
Type self-description
Polymorphic encoding ✅ (32 variants)
Self-validation
Multi-language consistency Partial Full
Blocklist filtering Not needed*

*idmix's polymorphic encoding makes blocklists unnecessary - just re-encode if you get an undesirable string.

Installation

Go:

go get github.com/Vanni-Fan/idmix/golang
Enter fullscreen mode Exit fullscreen mode

Python:

pip install vanni-idmix
Enter fullscreen mode Exit fullscreen mode

Rust:

cargo add idmix
Enter fullscreen mode Exit fullscreen mode

JavaScript:

npm install @vanni.fan/idmix
Enter fullscreen mode Exit fullscreen mode

Java:

<dependency>
    <groupId>io.github.vanni-fan</groupId>
    <artifactId>idmix</artifactId>
    <version>0.4.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

C#:

dotnet add package Vanni.Idmix
Enter fullscreen mode Exit fullscreen mode

PHP:

composer require vanni/idmix
Enter fullscreen mode Exit fullscreen mode

Use Cases

  • API Access Keys - Structured keys with embedded metadata
  • Short URLs - More compact than base64-encoded data
  • Invite Codes - Embed channel, validity period, etc.
  • Device Identification - Cross-platform unique identifiers
  • Order/Transaction IDs - Human-readable but information-dense

Conclusion

If you're building anything that needs short, type-safe, cross-language identifiers, give idmix a try. It's particularly well-suited for access key scenarios where you need to embed multiple typed fields.

The library is open source under Apache-2.0 license. I'd love your feedback and contributions!

GitHub: https://github.com/Vanni-Fan/idmix


Have you used Sqids or Hashids before? What limitations have you run into? Let me know in the comments!

Top comments (0)