Awesome Go

nanoid

CategoryUUID
SubcategoryUUID
Stars36

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/>

[![Go Report Card](https://goreportcard.com/badge/github.com/sixafter/nanoid)](https://goreportcard.com/report/github.com/sixafter/nanoid)
[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](LICENSE)
[![Go](https://img.shields.io/github/go-mod/go-version/sixafter/nanoid)](https://img.shields.io/github/go-mod/go-version/sixafter/nanoid)
[![Go Reference](https://pkg.go.dev/badge/github.com/sixafter/nanoid.svg)](https://pkg.go.dev/github.com/sixafter/nanoid)
[![FIPS-140 Mode Compatible](https://img.shields.io/badge/FIPS‑140--Mode-Compatible-brightgreen)](FIPS-140.md)

<br clear="right" />

## Status

[![Release](https://github.com/sixafter/nanoid/workflows/release/badge.svg)](https://github.com/sixafter/nanoid/actions)
[![CI](https://github.com/sixafter/nanoid/workflows/ci/badge.svg)](https://github.com/sixafter/nanoid/actions)
[![GitHub issues](https://img.shields.io/github/issues/sixafter/nanoid)](https://github.com/sixafter/nanoid/issues)

[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=six-after_nano-id&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=six-after_nano-id)
![CodeQL](https://github.com/sixafter/nanoid/actions/workflows/codeql-analysis.yaml/badge.svg)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=six-after_nano-id&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=six-after_nano-id)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/10826/badge)](https://www.bestpractices.dev/projects/10826)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/sixafter/nanoid/badge)](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/op

Frequently 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.

← Back to UUID