nanoid
Efficient, cryptographically secure generator for fast, concurrent NanoID and UUID creation
About nanoid
This large README is shown as a plain-text preview. Read the full README at the source
<img src="docs/nanoid.svg"
alt="NanoID Logo"
align="right"
style="max-width: 320px; min-width: 160px; width: 30%; height: auto; margin-left: 20px;" />
<h3><strong>nanoid: Tiny. Secure. Random.</strong></h3>
<br/>
[](https://goreportcard.com/report/github.com/sixafter/nanoid)
[](LICENSE)
[](https://img.shields.io/github/go-mod/go-version/sixafter/nanoid)
[](https://pkg.go.dev/github.com/sixafter/nanoid)
[](FIPS-140.md)
<br clear="right" />
## Status
[](https://github.com/sixafter/nanoid/actions)
[](https://github.com/sixafter/nanoid/actions)
[](https://github.com/sixafter/nanoid/issues)
[](https://sonarcloud.io/summary/new_code?id=six-after_nano-id)

[](https://sonarcloud.io/summary/new_code?id=six-after_nano-id)
[](https://www.bestpractices.dev/projects/10826)
[](https://scorecard.dev/viewer/?uri=github.com/sixafter/nanoid)
## Overview
A simple, fast, and efficient Go implementation of [Nano ID](https://github.com/ai/nanoid), a tiny, secure, URL-friendly, collision-resistant string generator.
Please see the [godoc](https://pkg.go.dev/github.com/sixafter/nanoid) for detailed documentation.
## Features
* Uses Go's `crypto/rand` and `x/crypto/chacha20` stream cipher package for generating cryptographically secure random numbers. This guarantees that the generated IDs are both unpredictable and suitable for security-sensitive applications.
- The custom Cryptographically Secure Pseudo Random Number Generator (CSPRNG) Includes a thread-safe global `Reader` for concurrent access.
- Up to 98% faster when using the `prng.Reader` as a source for v4 UUID generation using Google's [UUID](https://pkg.go.dev/github.com/google/uuid) package.
* Define your own set of characters for ID generation with a minimum length of 2 characters and maximum length of 256 characters.
* Define your own random number generator.
* Unicode and ASCII alphabets are supported.
* Designed to be safe for use in concurrent environments.
* Carefully structured to minimize heap allocations, reducing memory overhead and improving cache locality; crucial for applications where performance and resource usage are critical.
- 1 `allocs/op` for ASCII and Unicode alphabets regardless of alphabet size or generated ID length.
- 0 `allocs/op` for `Reader` interface across ASCII and Unicode alphabets regardless of alphabet size or generated ID length.
* Lightweight implementation with no external dependencies beyond the standard library other than for tests.
* Supports `io.Reader` Interface
- Used in contexts such as streaming data processing, pipelines, and other I/O-driven operations.
* Designed to run in FIPS‑140 validated environments using only Go standard library crypto.
- For FIPS‑140 compatible random number generation, use the [aes-ctr-drbg](https://github.com/sixafter/aes-ctr-drbg) module.
- See [FIPS‑140.md](FIPS-140.md) for details and deployment guidance.
Please see the [nanoid-cli](https://github.com/sixafter/nanoid-cli) for a command-line interface (CLI) that uses this module to generate Nano IDs.
## Verify with Cosign
[Cosign](https://github.com/sigstore/cosign) is used to sign releases for integrity verification.
To verify the integrity of the release, follow these steps:
```sh
# Fetch the latest release tag from GitHub API (e.g., "v1.65.1")
TAG=$(curl -s https://api.github.com/repos/sixafter/nanoid/releases/latest | jq -r .tag_name)
# Remove leading "v" for filenames (e.g., "v1.65.1" -> "1.65.1")
VERSION=${TAG#v}
# ---------------------------------------------------------------------
# Verify the source archive using Sigstore bundles
# ---------------------------------------------------------------------
# Download the release tarball and its signature bundle
curl -LO "https://github.com/sixafter/nanoid/releases/download/${TAG}/nanoid-${VERSION}.tar.gz"
curl -LO "https://github.com/sixafter/nanoid/releases/download/${TAG}/nanoid-${VERSION}.tar.gz.sigstore.json"
# Verify the tarball with Cosign using the published public key
cosign verify-blob \
--key "https://raw.githubusercontent.com/sixafter/nanoid/main/cosign.pub" \
--bundle "nanoid-${VERSION}.tar.gz.sigstore.json" \
"nanoid-${VERSION}.tar.gz"
# ---------------------------------------------------------------------
# Verify the checksums manifest using Sigstore bundles
# ---------------------------------------------------------------------
curl -LO "https://github.com/sixafter/nanoid/releases/download/${TAG}/checksums.txt"
curl -LO "https://github.com/sixafter/nanoid/releases/download/${TAG}/checksums.txt.sigstore.json"
cosign verify-blob \
--key "https://raw.githubusercontent.com/sixafter/nanoid/main/cosign.pub" \
--bundle "checksums.txt.sigstore.json" \
"checksums.txt"
# ---------------------------------------------------------------------
# Confirm local artifact integrity
# ---------------------------------------------------------------------
shasum -a 256 -c checksums.txt
```
If valid, Cosign will output:
```shell
Verified OK
```
## Installation
### Using `go get`
To install the Nano ID package, run the following command:
```sh
go get -u github.com/sixafter/nanoid
```
To use the NanoID package in your Go project, import it as follows:
```go
import "github.com/sixafter/nanoid"
```
## Usage
### Basic Usage with Default Settings
The simplest way to generate a Nano ID is by using the default settings. This utilizes the predefined alphabet and default ID length.
```go
package main
import (
"fmt"
"github.com/sixafter/nanoid"
)
func main() {
id, err := nanoid.New()
if err != nil {
panic(err)
}
fmt.Println("Generated ID:", id)
}
```
**Output**:
```bash
Generated ID: mGbzQkkPBidjL4IP_MwBM
```
### Generating a Nano ID with Custom length
Generate a NanoID with a custom length.
```go
package main
import (
"fmt"
"github.com/sixafter/nanoid"
)
func main() {
id, err := nanoid.NewWithLength(10)
if err != nil {
panic(err)
}
fmt.Println("Generated ID:", id)
}
```
**Output**:
```bash
Generated ID: 1A3F5B7C9D
```
### Using `io.Reader` Interface
Use the Nano ID generator as an `io.Reader`:
```go
package main
import (
"fmt"
"io"
"github.com/sixafter/nanoid"
)
func main() {
// Nano ID default length is 21
buf := make([]byte, nanoid.DefaultLength)
// Read a Nano ID into the buffer
_, err := nanoid.Read(buf)
if err != nil && err != io.EOF {
panic(err)
}
// Convert the byte slice to a string
id := string(buf)
fmt.Printf("Generated ID: %s\n", id)
}
```
**Output**:
```bash
Generated ID: 2mhTvy21bBZhZcd80ZydM
```
### Customizing the Alphabet and ID Length
Use the `WithAlphabet` to customize the alphabet.
```go
package main
import (
"fmt"
"github.com/sixafter/nanoid"
)
func main() {
// Define a custom alphabet
alphabet := "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
// Create a new generator with a custom alphabet and length hint
gen, err := nanoid.NewGenerator(
nanoid.WithAlphabet(alphabet),
nanoid.WithLengthHint(10),
)
if err != nil {
fmt.Println("Error creating Nano ID generator:", err)
return
}
// Generate a Nano ID using the custom generator
id, err := gen.New() // or gen.NewWithLength(10)
if err != nil {
fmt.Println("Error generating Nano ID:", err)
return
}
fmt.Println("Generated ID:", id)
}
```
**Output**"
```bash
Generated ID: G5J8K2M0QZ
```
### Customizing the Random Number Generator
Use the `WithRandReader` option to provide a custom random number generator.
```go
package main
import (
"crypto/rand"
"fmt"
"github.com/sixafter/nanoid"
)
func main() {
// Create a new generator with a custom random number generator
gen, err := nanoid.NewGenerator(
nanoid.WithRandReader(rand.Reader),
)
if err != nil {
fmt.Println("Error creating Nano ID generator:", err)
return
}
// Generate a Nano ID using the custom generator with
// the default length.
id, err := gen.New()
if err != nil {
fmt.Println("Error generating Nano ID:", err)
return
}
fmt.Println("Generated ID:", id)
}
```
**Output**"
```bash
Generated ID: A8I8K3J0QY
```
## Performance Optimizations
This benchmark highlights the performance optimizations achieved in this implementation of the Nano ID generator.
Conducted on an Apple M4 Max CPU with 16 cores, and the results demonstrate significant improvements in latency, throughput,
and memory allocations across various configurations.
| Mode | Latency (ns/op) | Throughput (IDs/sec) | Memory (B/op) | Allocs | Notes |
|:-------------------------------------| --------------: | -------------------: | ------------: | -----: | :-------------------------- |
| **Serial** | 74.1 | ~13.5 M | 24 | 1 | Single-threaded allocation |
| **Parallel (16 cores)** | 5.6 | ~178 M | 24 | 1 | Near-linear scalability |
| **Buffered Read (optimal 3–5 B)** | 25.0 | ~40 M | 0 | 0 | Fastest buffered config |
| **ASCII ID (21 chars)** | 54.0 | ~18.5 M | 0 | 0 | Default configuration |
| **Unicode ID (21 chars)** | 125.0 | ~8.0 M | 48 | 1 | UTF-8 overhead (~2× slower) |
For implementation details, benchmark results, and usage, see the CSPRNG [prng-chacha](https://github.com/sixafter/prng-chacha).
## Execute Benchmarks
Run the benchmarks using the `bench` make target:
```shell
make bench
```
### Interpreting Results:
Your output should look similar to the following:
<details>
<summary>Expand to see results</summary>
```shell
make bench
go test -bench=. -benchmem -memprofile=mem.out -cpuprofile=cpu.out
goos: darwin
goarch: arm64
pkg: github.com/sixafter/nanoid
cpu: Apple M4 Max
Benchmark_Allocations_Serial-16 14830308 74.10 ns/op 24 B/op 1 allocs/op
Benchmark_Allocations_Parallel-16 87374926 13.93 ns/op 24 B/op 1 allocs/op
Benchmark_Read_DefaultLength-16 21823753 54.01 ns/op 0 B/op 0 allocs/op
Benchmark_Read_VaryingBufferSizes/BufferSize_2-16 49202928 24.94 ns/op 0 B/op 0 allocs/op
Benchmark_Read_VaryingBufferSizes/BufferSize_3-16 47922843 25.39 ns/op 0 B/op 0 allocs/op
Benchmark_Read_VaryingBufferSizes/BufferSize_5-16 44769436 27.30 ns/op 0 B/op 0 allocs/op
Benchmark_Read_VaryingBufferSizes/BufferSize_13-16 31385779 37.98 ns/op 0 B/op 0 allocs/op
Benchmark_Read_VaryingBufferSizes/BufferSize_21-16 24563296 49.45 ns/op 0 B/op 0 allocs/op
Benchmark_Read_VaryingBufferSizes/BufferSize_34-16 17664668 67.80 ns/op 0 B/op 0 allocs/op
Benchmark_Read_ZeroLengthBuffer-16 986404063 1.212 ns/op 0 B/op 0 allocs/op
Benchmark_Read_Concurrent/Concurrency_1-16 23469319 50.41 ns/op 0 B/op 0 allocs/op
Benchmark_Read_Concurrent/Concurrency_2-16 47287224 25.17 ns/op 0 B/op 0 allocs/op
Benchmark_Read_Concurrent/Concurrency_4-16 89502978 13.22 ns/op 0 B/op 0 allocs/op
Benchmark_Read_Concurrent/Concurrency_8-16 123994070 9.912 ns/op 0 B/op 0 allocs/op
Benchmark_Read_Concurrent/Concurrency_16-16 209096799 5.656 ns/op 0 B/op 0 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen2/IDLen8-16 31861614 37.86 ns/op 8 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen2/IDLen16-16 22977042 51.86 ns/op 16 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen2/IDLen21-16 18545468 64.42 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen2/IDLen32-16 15435464 76.98 ns/op 32 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen2/IDLen64-16 9426519 126.7 ns/op 64 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen2/IDLen128-16 5483312 217.6 ns/op 128 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen16/IDLen8-16 32191970 37.13 ns/op 8 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen16/IDLen16-16 22994911 51.37 ns/op 16 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen16/IDLen21-16 18733461 64.11 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen16/IDLen32-16 15541071 76.51 ns/op 32 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen16/IDLen64-16 9399879 127.0 ns/op 64 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen16/IDLen128-16 5479530 218.4 ns/op 128 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen32/IDLen8-16 32642143 37.36 ns/op 8 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen32/IDLen16-16 23300292 51.40 ns/op 16 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen32/IDLen21-16 18884118 63.52 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen32/IDLen32-16 15857773 75.56 ns/op 32 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen32/IDLen64-16 9551781 126.2 ns/op 64 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen32/IDLen128-16 5524531 217.5 ns/op 128 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen64/IDLen8-16 32380906 37.33 ns/op 8 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen64/IDLen16-16 22809477 51.54 ns/op 16 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen64/IDLen21-16 18773943 63.74 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen64/IDLen32-16 15882687 75.79 ns/op 32 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen64/IDLen64-16 9416259 126.7 ns/op 64 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/ASCII_AlphabetLen64/IDLen128-16 5503002 217.8 ns/op 128 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen2/IDLen8-16 16668662 70.04 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen2/IDLen16-16 11526090 104.1 ns/op 48 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen2/IDLen21-16 9527602 125.2 ns/op 48 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen2/IDLen32-16 6898809 174.3 ns/op 80 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen2/IDLen64-16 3853819 312.6 ns/op 144 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen2/IDLen128-16 2084970 575.9 ns/op 289 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen16/IDLen8-16 17133789 69.94 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen16/IDLen16-16 11481788 105.0 ns/op 48 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen16/IDLen21-16 9534361 125.9 ns/op 48 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen16/IDLen32-16 6850174 174.2 ns/op 80 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen16/IDLen64-16 3839973 311.8 ns/op 144 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen16/IDLen128-16 2088130 574.6 ns/op 289 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen32/IDLen8-16 17156673 70.04 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen32/IDLen16-16 11410390 105.0 ns/op 48 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen32/IDLen21-16 9537810 125.3 ns/op 48 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen32/IDLen32-16 6809893 175.9 ns/op 80 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen32/IDLen64-16 3833170 313.1 ns/op 144 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen32/IDLen128-16 2081092 574.3 ns/op 289 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen64/IDLen8-16 16723262 71.30 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen64/IDLen16-16 11270502 105.6 ns/op 48 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen64/IDLen21-16 9602179 125.9 ns/op 48 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen64/IDLen32-16 6761265 176.4 ns/op 80 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen64/IDLen64-16 3848028 312.4 ns/op 144 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Serial/Unicode_AlphabetLen64/IDLen128-16 2083189 576.3 ns/op 289 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Parallel/ASCII_AlphabetLen2/IDLen8-16 194620867 6.209 ns/op 8 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Parallel/ASCII_AlphabetLen2/IDLen16-16 141351794 9.845 ns/op 16 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Parallel/ASCII_AlphabetLen2/IDLen21-16 100000000 11.55 ns/op 24 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Parallel/ASCII_AlphabetLen2/IDLen32-16 81193770 14.75 ns/op 32 B/op 1 allocs/op
Benchmark_Alphabet_Varying_Parallel/ASCII_AlphabetLen2/IDLen64-16 45330201 26.13 ns/op 64 B/op 1 allocs/opFrequently Asked Questions
What is nanoid?
nanoid is a UUID library for the Go programming language. Efficient, cryptographically secure generator for fast, concurrent NanoID and UUID creation
How do I install nanoid?
Install nanoid with the Go module system using `go get sixafter/nanoid`. Check the repository for the current installation instructions.
What category does nanoid belong to?
nanoid is listed under UUID, specifically UUID.