_ _ _ _ ___ ____ _ __
| | | || | | ||_ _| _ \ | |/ /___ _ _
| | | || | | | | || | | | | ' // _ \ | | |
| |_| || |_| | | || |_| | | . \ __/ |_| |
\___/ \___/ |___|____/ |_|\_\___|\__, |
|___/
The uuidkey
package encodes UUIDs to a readable Key
format via the Base32-Crockford codec.
Note: Thanks to everyone for the feedback and suggestions from the original article, we learned a lot and made improvements to follow the GitHub Secret Scanning format (with checksum) and added additional entropy options to ensure UUIDv7 encoding can be used in a wide variety of use cases. You can still use the
Encode
function to generate aKey
type without the GitHub prefix format and additional entropy - but we recommend using theNewAPIKey
function with an 8 character prefix to stay symetrical ;P
The uuidkey
package generates secure, readable API keys by encoding UUIDs using Base32-Crockford with additional security features.
You can use the uuidkey
package to generate API keys for your application using the NewAPIKey
function (recommended to guarantee at least 128 bits of entropy and follow the GitHub Secret Scanning format) or the Encode
function (to generate just a Key
type).
AGNTSTNP_38QARV01ET0G6Z2CJD9VA2ZZAR0XJJLSO7WBNWY3F_A1B2C3D8
└──────┘ └──────────────────────────┘└───────────┘ └──────┘
Prefix Crock32 Encoded UUID Entropy Checksum
- Prefix - Company/application identifier (e.g., "AGNTSTNP")
- UUID Key - Base32-Crockford encoded UUID
- Entropy - Additional random data (128, 160, or 256 bits)
- Checksum - CRC32 checksum (8 characters) for validation
- Secret Scanning - Formatted for GitHub Secret Scanning detection
- Validation - CRC32 checksum for error detection and validation
- Entropy Options - Configurable entropy levels that ensure UUIDv7 security (128, 160, or 256 bits)
Compatible with any UUID library following RFC 4122 specification. Officially tested with:
- github.com/gofrs/uuid (v4.4.0+)
- github.com/google/uuid (v1.6.0+)
To install the uuidkey
package, use the following command:
go get github.com/agentstation/uuidkey
To use the uuidkey
package in your Go code, follow these steps:
- Import the package:
import "github.com/agentstation/uuidkey"
- Create and parse API Keys:
// Create a new API Key with default settings (160-bit entropy)
apiKey := uuidkey.NewAPIKey("AGNTSTNP", "d1756360-5da0-40df-9926-a76abff5601d")
fmt.Println(apiKey) // Output: AGNTSTNP_38QARV01ET0G6Z2CJD9VA2ZZAR0XJJLSO7WBNWY3F_A1B2C3D8
// Create an API Key with 128-bit entropy
apiKey = uuidkey.NewAPIKey("AGNTSTNP", "d1756360-5da0-40df-9926-a76abff5601d", uuidkey.With128BitEntropy)
fmt.Println(apiKey) // Output: AGNTSTNP_38QARV01ET0G6Z2CJD9VA2ZZAR0XJJLSO7WBNWY3F_A1B2C3D8
// Parse an existing API Key
apiKey, err := uuidkey.ParseAPIKey("AGNTSTNP_38QARV01ET0G6Z2CJD9VA2ZZAR0XJJLSO7WBNWY3F_A1B2C3D8")
if err != nil {
log.Fatal("Error:", err)
}
fmt.Printf("Prefix: %s, Key: %s, Entropy: %s\n", apiKey.Prefix, apiKey.Key, apiKey.Entropy)
- Work with UUID Keys directly:
// With hyphens (default)
key, _ := uuidkey.Encode("d1756360-5da0-40df-9926-a76abff5601d")
fmt.Println(key) // Output: 38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X
// Without hyphens
key, _ := uuidkey.Encode("d1756360-5da0-40df-9926-a76abff5601d", uuidkey.WithoutHyphens)
fmt.Println(key) // Output: 38QARV01ET0G6Z2CJD9VA2ZZAR0X
- Decode a Key type to a UUID string with Key format validation:
// With hyphens
key, _ := uuidkey.Parse("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X")
uuid, err := key.UUID()
if err != nil {
log.Fatal("Error:", err)
}
fmt.Println(uuid) // Output: d1756360-5da0-40df-9926-a76abff5601d
// Without hyphens
key, _ := uuidkey.Parse("38QARV01ET0G6Z2CJD9VA2ZZAR0X")
uuid, err := key.UUID()
if err != nil {
log.Fatal("Error:", err)
}
fmt.Println(uuid) // Output: d1756360-5da0-40df-9926-a76abff5601d
- Decode a Key type to a UUID string with only basic Key length validation:
key, _ := uuidkey.Parse("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X")
uuid, err := key.Decode()
if err != nil {
log.Fatal("Error:", err)
}
fmt.Println(uuid) // Output: d1756360-5da0-40df-9926-a76abff5601d
- Work directly with UUID bytes:
// Convert UUID string to bytes
uuidStr := "d1756360-5da0-40df-9926-a76abff5601d"
uuidBytes, err := hex.DecodeString(strings.ReplaceAll(uuidStr, "-", ""))
if err != nil {
log.Fatal("Error:", err)
}
// Convert to [16]byte array
var uuid [16]byte
copy(uuid[:], uuidBytes)
// Now use the bytes with EncodeBytes (with hyphens)
key, _ := uuidkey.EncodeBytes(uuid)
fmt.Println(key) // Output: 38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X
// Without hyphens
key, _ := uuidkey.EncodeBytes(uuid, uuidkey.WithoutHyphens)
fmt.Println(key) // Output: 38QARV01ET0G6Z2CJD9VA2ZZAR0X
// Convert Key back to UUID bytes
key, _ := uuidkey.Parse("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X")
bytes, err := key.Bytes()
if err != nil {
log.Fatal("Error:", err)
}
fmt.Printf("%x", bytes) // Output: d17563605da040df9926a76abff5601d
import "github.com/agentstation/uuidkey"
Package uuidkey encodes UUIDs to a readable Key format via the Base32-Crockford codec.
- Constants
- type APIKey
- type Key
- func Encode(uuid string, opts ...Option) (Key, error)
- func EncodeBytes(uuid [16]byte, opts ...Option) (Key, error)
- func Parse(key string) (Key, error)
- func (k Key) Bytes() ([16]byte, error)
- func (k Key) Decode() (string, error)
- func (k Key) IsValid() bool
- func (k Key) String() string
- func (k Key) UUID() (string, error)
- type Option
Key validation constraint constants
const (
// KeyLengthWithHyphens is the total length of a valid UUID Key, including hyphens.
KeyLengthWithHyphens = 31 // 7 + 1 + 7 + 1 + 7 + 1 + 7 = 31 characters
// KeyLengthWithoutHyphens is the total length of a valid UUID Key, excluding hyphens.
KeyLengthWithoutHyphens = 28 // 7 + 7 + 7 + 7 = 28 characters
// KeyPartLength is the length of each part in a UUID Key.
// A UUID Key consists of 4 parts separated by hyphens.
KeyPartLength = 7
// KeyHyphenCount is the number of hyphens in a valid UUID Key.
KeyHyphenCount = 3
// KeyPartsCount is the number of parts in a valid UUID Key.
KeyPartsCount = KeyHyphenCount + 1
// UUIDLength is the standard length of a UUID string, including hyphens.
// Reference: RFC 4122 (https://tools.ietf.org/html/rfc4122)
UUIDLength = 36
)
type APIKey
APIKey represents a compound key consisting of four parts or segments: - Prefix: A company or application identifier (e.g., "AGNTSTNP") - Key: A UUID-based identifier encoded in Base32-Crockford - Entropy: Additional segment of random data for increased uniqueness - Checksum: CRC32 checksum of the previous components (8 characters)
Format:
[Prefix]_[UUID Key][Entropy]_[Checksum]
AGNTSTNP_38QARV01ET0G6Z2CJD9VA2ZZAR0XJJLSO7WBNWY3F_A1B2C3D8
└─────┘ └──────────────────────────┘└────────────┘ └──────┘
Prefix UUID-based identifier Entropy Checksum
type APIKey struct {
Prefix string
Key Key
Entropy string
Checksum string
}
func NewAPIKey
func NewAPIKey(prefix, uuid string, opts ...Option) (APIKey, error)
NewAPIKey creates a new APIKey from a string prefix, string UUID, and options.
func NewAPIKeyFromBytes
func NewAPIKeyFromBytes(prefix string, uuid [16]byte, opts ...Option) (APIKey, error)
NewAPIKeyFromBytes creates a new APIKey from a string prefix, [16]byte UUID, and options.
func ParseAPIKey
func ParseAPIKey(apikey string) (APIKey, error)
ParseAPIKey will parse a given APIKey string into an APIKey type.
func (APIKey) String
func (a APIKey) String() string
String returns the complete API key as a string with all components joined
type Key
Key is a UUID Key string.
type Key string
func Encode
func Encode(uuid string, opts ...Option) (Key, error)
Encode will encode a given UUID string into a Key. It pre-allocates the exact string capacity needed for better performance.
func EncodeBytes
func EncodeBytes(uuid [16]byte, opts ...Option) (Key, error)
EncodeBytes encodes a [16]byte UUID into a Key.
func Parse
func Parse(key string) (Key, error)
Parse converts a Key formatted string into a Key type.
func (Key) Bytes
func (k Key) Bytes() ([16]byte, error)
Bytes converts a Key to a [16]byte UUID.
func (Key) Decode
func (k Key) Decode() (string, error)
Decode will decode a given Key into a UUID string with basic length validation.
func (Key) IsValid
func (k Key) IsValid() bool
IsValid verifies if a given Key follows the correct format. The format should be:
- 31 characters long (with hyphens) or 28 characters (without hyphens)
- Uppercase
- Contains only alphanumeric characters
- Contains 3 hyphens (if hyphenated)
- Each part is 7 characters long
- Each part contains only valid crockford base32 characters (I, L, O, U are not allowed)
func (Key) String
func (k Key) String() string
String will convert your Key into a string.
func (Key) UUID
func (k Key) UUID() (string, error)
UUID will validate and convert a given Key into a UUID string.
type Option
Option is a function that configures options
type Option func(c *config)
With128BitEntropy expects 128 bits of entropy in the APIKey
var With128BitEntropy Option = func(c *config) {
c.entropySize = EntropyBits128
}
With160BitEntropy expects 160 bits of entropy in the APIKey
var With160BitEntropy Option = func(c *config) {
c.entropySize = EntropyBits160
}
With256BitEntropy expects 256 bits of entropy in the APIKey
var With256BitEntropy Option = func(c *config) {
c.entropySize = EntropyBits256
}
WithoutHyphens expects no hyphens in the Key
var WithoutHyphens Option = func(c *config) {
c.hyphens = false
}
Generated by gomarkdoc
jack@devbox ➜ make help
Usage:
make <target>
General
help Display the list of targets and their descriptions
Tooling
install-devbox Install Devbox
devbox-update Update Devbox
devbox Run Devbox shell
Installation
install Download go modules
Development
fmt Run go fmt
generate Generate and embed go documentation into README.md
vet Run go vet
lint Run golangci-lint
Benchmarking, Testing, & Coverage
bench Run Go benchmarks
test Run Go tests
coverage Run tests and generate coverage report
Note: These benchmarks were run on an Apple M2 Max CPU with 12 cores (8 performance and 4 efficiency) and 32 GB of memory, running macOS 14.6.1. The results are not representative of all systems, but should give you a general idea of the performance of the package. I was running other processes on the machine while running the benchmarks.
Your mileage may vary.
devbox ➜ make bench
Running go benchmarks...
go test ./... -tags=bench -bench=.
goos: darwin
goarch: arm64
pkg: github.com/agentstation/uuidkey
BenchmarkValidate-12 40536168 29.66 ns/op
BenchmarkValidateInvalid-12 820111176 1.439 ns/op
BenchmarkParse-12 38579367 29.78 ns/op
BenchmarkParseInvalid-12 668715536 1.777 ns/op
BenchmarkUUID-12 4767662 250.6 ns/op
BenchmarkUUIDInvalid-12 66099007 17.17 ns/op
BenchmarkEncode-12 7224882 162.9 ns/op
BenchmarkDecode-12 5363344 220.4 ns/op
BenchmarkBytes-12 5438636 217.0 ns/op
BenchmarkEncodeBytes-12 12546094 94.13 ns/op
BenchmarkValidateWithHyphens-12 38416134 29.51 ns/op
BenchmarkValidateWithoutHyphens-12 39153255 29.23 ns/op
BenchmarkParseWithHyphens-12 38402560 30.19 ns/op
BenchmarkParseWithoutHyphens-12 38653306 29.85 ns/op
BenchmarkEncodeWithHyphens-12 7146574 164.0 ns/op
BenchmarkEncodeWithoutHyphens-12 7255610 163.3 ns/op
BenchmarkDecodeWithHyphens-12 5368426 221.0 ns/op
BenchmarkDecodeWithoutHyphens-12 5370716 221.0 ns/op
BenchmarkBytesWithHyphens-12 5430710 220.6 ns/op
BenchmarkBytesWithoutHyphens-12 5032964 217.1 ns/op
BenchmarkEncodeBytesWithHyphens-12 12419739 92.85 ns/op
BenchmarkEncodeBytesWithoutHyphens-12 12544892 92.87 ns/op
BenchmarkString-12 1000000000 0.2875 ns/op
BenchmarkValidateInvalidFormat-12 824398530 1.434 ns/op
BenchmarkParseInvalidFormat-12 668982553 1.777 ns/op
BenchmarkDecodeInvalidFormat-12 10187534 114.5 ns/op
BenchmarkEncodeInvalidUUID-12 11438924 102.3 ns/op
BenchmarkBytesInvalidFormat-12 10280540 113.4 ns/op
PASS
ok github.com/agentstation/uuidkey 36.679s