Contributing
Contributions are welcome: bug reports, fixes, docs, and new language ports. Open an issue to discuss anything substantial first, then send a pull request.
Running the tests
Each language lives in its own directory and has its own toolchain.
Go
cd go
go test ./...
go vet ./...
Python
cd python
uv sync
uv run pytest
uv run ruff check
To run against every supported Python version (3.10–3.14):
uv run tox
JavaScript / TypeScript
cd js
npm install
npm test
npm run typecheck
Adding a new language
The three implementations are wire-compatible: the same (secret, scope, value, prefix) produces the same id everywhere. A new port has to reproduce that exact output, so the bar is "passes the shared parity vectors."
The target language needs:
- a sqids port,
- HMAC-SHA256 (standard library or an audited package).
Steps:
-
Read the wire format. It is the contract. See the Overview, Payload layout, and Algorithm sections on the documentation home page. In short: the payload is
VERSION + TAG + body + MAC, whereMACis the first 4 bytes ofHMAC-SHA256(secret, scope + 0x00 + signed). The payload is split into 6-byte big-endian chunks (a length number first), then encoded with sqids using a per-scope alphabet derived from the secret, with the prefix prepended. -
Implement the API. Match the existing surface:
encode,decode,try_decode, the*_manybatch variants, and the options (min_length,base_alphabet,previous_secrets). Also should include the scope bound option. -
Verify against the parity vectors. Load
fixtures/parity_vectors.jsonand, for every entry, assertencode(scope, value, prefix) == idand that decodingidreturns the originalvalue. This is the conformance test that proves the port matches the others. Each value carries atype(int,str,bytes,uuid) and is stored as a string: integers as decimal, bytes as hex, UUIDs in canonical form. -
Wire it up. Add a directory README, a docs page, and a CI workflow modelled on the existing ones.
If the language lacks a sqids port, that is the first thing to build (or port). Everything else depends on it producing identical output.