GCID
Global, Cryptographic, Identifiers
Quick examples
from gcid import registry, typed_id
from gcid.gcid import id_to_db_seq
ids = registry(profile="prf", asset="asset")
profile_id = ids.profile(123)
asset_id = ids.asset.from_seq(456)
str(profile_id)
# "prf_QBt6L5GZA4ob6M8wjQ5MWtgochh"
profile_id.seq
# 123
str(asset_id)
# "asset_GLCN4aqgfwoCQT4hcKShEcgFyXw"
ids.asset.to_seq(asset_id)
# 456
IDs can also carry a 7-byte location partition inside the encrypted payload. The public string still only exposes the type prefix and ciphertext:
RegionalAssetId = typed_id("asset", "asset", location=42)
regional_asset_id = RegionalAssetId(123)
str(regional_asset_id)
# "asset_43XfxRWqPm4Tu4iYuGbt6BawSD4h"
regional_asset_id.seq
# 123
regional_asset_id.location_partition.hex()
# "0000000000002a"
id_to_db_seq(regional_asset_id, RegionalAssetId).location.hex()
# "0000000000002a"
Conversion benchmark
Run the local benchmark to estimate conversion costs on your machine:
uv run bench
For a faster smoke run:
GCID_BENCH_N=10000 uv run bench
To include cProfile output for the conversion workload:
GCID_BENCH_N=10000 GCID_BENCH_PROFILE=1 uv run bench
The benchmark reports raw encode/decode costs, typed ID construction and .seq
access, pydantic model validation costs, and base58-only encode/decode costs in
microseconds per operation.
Recent local profile-guided optimization results, measured with
GCID_BENCH_N=50000:
| Operation | Before | After |
|---|---|---|
seq_to_id |
5.744 us/op | 5.667 us/op |
id_to_seq |
7.062 us/op | 6.316 us/op |
| typed ID from seq | 13.668 us/op | 6.713 us/op |
typed ID .seq |
7.383 us/op | 0.021 us/op |
| pydantic validation | 23.302 us/op | 16.281 us/op string / 7.625 us/op typed |
The post-optimization profile shows the remaining dominant costs are base58 encoding/decoding, pydantic validation, and cryptography context creation. The benchmark also reports base58-only costs; in the same run they were about 2.998 us/op for encode and 3.419 us/op for decode.
Crypto validation
GCIDv2 stores a clear binary header in the Base58 payload, then encrypts the location partition and sequence number with AES-256-GCM-SIV. The visible type prefix and binary header are authenticated as associated data before decoded values are trusted.
Encode
type prefix header location database seq
"asset" 02 01 01 00 00..2a 123
| | | |
+--------------+-------------+------------+
|
v
AEAD associated data: prefix + header
|
v
AES-256-GCM-SIV encrypt
|
v
+-- header[4] --+-- ciphertext --+-- tag[16] --+
|
v
base58 encode
|
v
"asset_CbdrzuzUWxA1FkCVjXXNP92ZT5cpu2DrP23ioGdJ5GPWj2M"
Decode / validate
"asset_CbdrzuzUWxA1FkCVjXXNP92ZT5cpu2DrP23ioGdJ5GPWj2M"
|
v
split prefix + base58 body
|
v
parse clear header
|
v
authenticate prefix + header + payload
|
+----------+----------+
| |
v v
reject AES-256-GCM-SIV decrypt
|
v
location, seq
Global
Identifiers contain internal location tagging information that can be used in a federated system to locate objects globally while maintaining private internal number spaces.
Cryptographic
Identifiers can be reversed to provide metadata such as row number and shard without exposing this information to external observers.
Identifiers
Strings that can be used with APIs such as is seen in APIs from Stripe, et al.
Typed IDs
Applications can define their own ID vocabulary once and use those types directly:
from gcid import registry
ids = registry(profile="prf", asset="asset")
profile_id = ids.profile(123)
asset_id = ids.asset.from_seq(456)
str(profile_id)
profile_id.seq
ids.asset.to_seq(asset_id)
Typed IDs are string subclasses, so they can be returned directly from API models while still validating that the prefix and encrypted payload match the expected type.
Pydantic
Typed IDs validate natively in pydantic models:
from pydantic import BaseModel
from gcid import registry
ids = registry(profile="prf", asset="asset")
class Asset(BaseModel):
id: ids.asset
owner_id: ids.profile
asset = Asset(id="asset_...", owner_id="prf_...")
asset.id.seq
asset.model_dump()
Pydantic validation accepts GCID strings by default. Direct construction accepts
integers for internal use, e.g. ids.asset(123), but pydantic fields reject raw
sequence numbers unless the type is created with accept_seq_in_pydantic=True.
Releases
This repository hosts several independently versioned language packages:
| Package | Path | Release tag prefix | Consumed via |
|---|---|---|---|
Python gcid |
repo root | python/vX.Y.Z |
PyPI (pip install gcid) |
Go gcid |
go/gcid |
go/gcid/vX.Y.Z |
Go module proxy (go get github.com/jrepp/gcid/go/gcid@vX.Y.Z) |
Release Please reads
conventional commits on main and
opens a release pull request that bumps versions, updates changelogs, and,
once merged, creates a prefixed tag and GitHub release for each changed
package. Commit messages that only affect one package should be scoped
accordingly (for example feat(go): ...) so each package is versioned on its
own cadence.
When a python/* release is cut, the release workflow builds the package and
publishes it to PyPI. Go packages need no upload: a go/gcid/vX.Y.Z tag is
resolved directly by the Go module proxy.
Metadata
Release files for gcid 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gcid-0.2.0.tar.gz | 84.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gcid-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 93.7 kB
Release files / gcid-0.2.0.tar.gz
| Download URL | gcid-0.2.0.tar.gz |
|---|---|
| Size | 84.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c1c871fd00b7c24dfbde355cbf66982bda10cf80985f5bab6007c873554bcab8
|
|
BLAKE2b-256 checksum How to use checksums |
15c8448b735e3e3fdf9f3fae12685de674465a2dcf5843ae92b619e7f8cdbf24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / gcid-0.2.0-py3-none-any.whl
| Download URL | gcid-0.2.0-py3-none-any.whl |
|---|---|
| Size | 9.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
78c4880ec26fdb90a101878b1c373f62bdbf3a05bc5d9c735839a72bcf44b052
|
|
BLAKE2b-256 checksum How to use checksums |
403ac4a2e88e9cbaf5f727853fbf65dd60499d64e12f8913cc352da2af2e1a90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency log