dealcode¶
Collision-free, random-looking codes from a counter — like dealing cards from a shuffled deck. Every card comes out exactly once; the order looks random; the dealer only remembers how many cards have been dealt.
Anyone who has shipped short public codes — an airline-style booking
reference, an order number, a cus_xxxxxx shortcode — knows the trap:
- Random? The birthday problem bites absurdly early: draw random 6-digit codes and the first duplicate is expected around code #1,200 — in a space of a million. From then on, every insert carries a uniqueness check and a retry loop.
- UUID? Never collides, but 36 characters — not something you print on a boarding pass.
- nanoid? Shorter, yet still long — it has to be, because it is random. Shrink it and the birthday problem comes straight back.
- A raw sequence? Short and collision-free — and it broadcasts exactly how many orders you have.
dealcode is the missing option: keep the sequence your database already produces, and it packs the code space full — every code dealt exactly once, no repeats until all 1,000,000 codes (then all 10,000,000, …) are actually used — while the order stays cryptographically unpredictable from outside. All you need is a counter. (Full argument and alternatives table: Why dealcode exists.)
counter: 0 1 2 3 ... 16,777,216
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
code: d568e1 f7f229 0f868d f37ff8 ... 7b11743 ← grew to 7 chars
only when 6 ran out
Give dealcode a never-repeating integer (a database sequence, an auto-increment id) and a secret key; it gives you a short code with four properties:
-
Never collides
The mapping is a keyed permutation (FF1, NIST SP 800-38G), so uniqueness is mathematical, not probabilistic. No retry loops, no birthday problem, no
UNIQUE-violation handling as a code path. -
Doesn't leak your numbers
Sequential inputs produce scattered, unpredictable outputs. Order volume, issue rate, and "how many came before me" stay private — no German tank problem.
-
Stays as short as possible
Codes start at 6 characters (configurable) and grow by one character only when the current length is exhausted.
-
Decodes back
With the key, a code maps back to its counter. Look up
orders WHERE id = decode(code)— no extra column index required, and obviously-invalid codes are rejected before touching the database.
The same key + config produces the same mapping in every language. The specification is normative, and shared test vectors keep all seven implementations bit-identical.
Sixty-second tour¶
Python shown; every language mirrors it:
from dealcode import Dealcode
codec = Dealcode(key="use `openssl rand -hex 32` in production")
codec.encode(0) # 'd568e1'
codec.encode(1) # 'f7f229'
codec.decode("f7f229") # 1
Pick the shape your product needs:
Dealcode(key, "crockford", domain="coupons") # e.g. 'ZV6NQ0' — human-friendly, confusables handled
Dealcode(key, "dec", domain="orders") # e.g. '839207' — digits only
Dealcode(key, "base62", min_length=8) # e.g. 'tHx93bQk'
Dealcode(key, "hex", min_length=16, max_length=16) # fixed-length tokens
CyclingDealcode(key, "crockford", length=6) # fixed forever — reuses the space per cycle (see guide)
Dealcode(key, "!@#$%^&*") # your own alphabet, why not
Seven implementations, one mapping¶
| Language | Directory | Install | Crypto dependency |
|---|---|---|---|
| Python | python/ |
pip install dealcode |
cryptography (PyCA) |
| TypeScript / JavaScript | js/ |
npm install dealcode |
node:crypto (built-in) |
| Go | go/ |
go get github.com/algorix-hq/dealcode/go |
standard library |
| Java | java/ |
Maven io.algorix:dealcode |
JCE (built-in) |
| Rust | rust/ |
cargo add dealcode |
RustCrypto aes, sha2 |
| C | c/ |
vendored / static lib | OpenSSL libcrypto |
| C++ | cpp/ |
wraps the C core | OpenSSL libcrypto |
Registry status
v1.0.1 is live on PyPI,
npm,
crates.io, and
Maven Central;
go get resolves from GitHub directly. C and C++ are vendored by
design (see each language page).
Everything else is dependency-free by design: FF1 and the dealcode layer are
implemented from the NIST specification in each language and validated
against the official NIST sample vectors plus this repo's shared vectors
(testvectors/).
How it works¶
encode(n) picks the code length d by range (counter < 16^6 → 6 hex
chars, < 16^7 → 7, ...), writes n as a d-digit number, and encrypts
those digits with FF1 — format-preserving encryption that outputs another
d-digit number under your key. Same-length codes can't collide because
encryption is a bijection; different-length codes can't collide because they
have different lengths. decode runs it backwards and validates strictly.
Details: Specification · rationale: Design decisions · problem statement: Why dealcode exists.
When to use it — and when not to¶
Use dealcode for order numbers, coupon and invite codes, ticket numbers, support PINs, shortlinks: things that must be unique, short, and non-revealing, where you already have (or can trivially add) a counter.
Do not use it for session tokens, API keys, or password-reset links — the code space is deliberately small, so use ≥128-bit random tokens for anything that authenticates. The full reasoning and an alternatives table live in Why dealcode exists; the threat model is spelled out in the security model.
One rule to remember
Key, alphabet, lengths, and domain are frozen the moment the first code ships. Changing any of them for an existing namespace can collide with already-issued codes. New scheme → new domain (or new key + new namespace).
For AI coding agents¶
Using an AI assistant to write code against dealcode? Give it the docs in
agent-readable form — llms.txt
(index) and
llms-full.txt
(the entire documentation, spec included, as one file) — and install the
usage-rules skill so it knows the operational invariants (frozen config,
decode semantics, cycling mode):
License¶
MIT © Algorix Corporation.