Julien Sellier

Last update: 2025-12-10

Generating (Human-Friendly) IDs in Go

Goals:

Default charset (16 characters): "2346789CHJKPTVXY".

package huid

import (
	"crypto/rand"
	"errors"
	"fmt"
	"io"
)

const Charset = "2346789CHJKPTVXY"

var (
	ErrBadCharset = errors.New("huid: bad charset")
	ErrReadPRNG   = errors.New("huid: read from PRNG")
)

func init() {
	if len(Charset) != 16 {
		panic(ErrBadCharset)
	}
}

// Generates a human-friendly ID using that only contains "safe" non-confusable characters
// and no vowels (to avoid generating human words).
// The character set is made from 16 alpha-numeric visually distinct ASCII characters.
// NB: One byte of entropy (N) will result in 2 characters of textual output (2N).
func New(n int) (id string) {
	b := make([]byte, n)
	_, err := io.ReadFull(rand.Reader, b)
	if err != nil {
		panic(fmt.Errorf("%w: %w", ErrReadPRNG, err))
	}

	for _, v := range b {
		id += string(Charset[(v&0xF0)>>4])
		id += string(Charset[(v&0x0F)>>0])
	}

	return id
}

NB: Since base-16 encoding is used, one BYTE (not bits) of entropy results in two output BYTES in the New function below.