Coder Provisioner Keys — How They’re Generated & Secured

Last updated: June 25, 2026

Audience: Support, Customers, and Operators

Summary (TL;DR):

  1. Provisioner keys are 43-character, random and derived from a character set of 62 characters (a-z, A-Z and 0-9).
  2. Keys are generated with a cryptographically secure RNG (OS CSPRNG). This is /dev/urandom on Linux, arc4random on MacOS and CryptGenRandom on Windows.
  3. Coder stores only the SHA-256 hash of the key. It doesn't store the plaintext.

How key generation works:

  1. Secret creation: This happens within the file linked here. The most relevant code snippet can be found below:
const (
 secretLength = 43
)

func New(organizationID uuid.UUID, name string, tags map[string]string) (database.InsertProvisionerKeyParams, string, error) {
 secret, hashed, err := apikey.GenerateSecret(secretLength)
 if err != nil {
 return database.InsertProvisionerKeyParams{}, "", xerrors.Errorf("generate secret: %w", err)
 }

 if tags == nil {
 tags = map[string]string{}
 }

 return database.InsertProvisionerKeyParams{
 ID:             uuid.New(),
 CreatedAt:      dbtime.Now(),
 OrganizationID: organizationID,
 Name:           name,
 HashedSecret:   hashed,
 Tags:           tags,
 }, secret, nil
}

As we can see, We have a 43 character random string which is generated and returned along with the SHA-256 hash of the secret. ONLY the hashed secret is stored within the database as can be seen from the last part of the above code snippet.

  1. The random string generation happens within the file linked here. We make use of Go's crypto/rand package and the character set contains 62 characters (a-z, A-Z and 0-9) which is 26 + 26 + 10 = 62.
  2. We hash the secret here and we use the industry grade SHA-256 hashing algorithm. Coder stores only the SHA-256 hash (plus metadata like organization, name, tags). The plaintext key is returned once at creation time and is never stored.
  3. On use, the presented key is hashed with SHA-256 and compared to the stored hash using a constant-time comparison to prevent timing attacks.
     

Why 43 characters?

  • Search space / Number of possible keys = 62^43 = 1.18 * 10^77 - (We have 62 characters that need to be filled in 43 spots)

  • Entropy: log base 2 (62^43) = 43 log base 2(62) = 43 * 5.954 = 256.022 (256 bits)

That’s comparable to a 256-bit symmetric key and provides a very large safety margin against brute-force attacks.

Randomness & Uniformity

  • CSPRNG sources: The generator draws from the operating system’s secure facilities (e.g., /dev/urandom on Linux, arc4random on macOS, and Windows CNG).

  • Unbiased selection: Characters are produced with an unbiased mapping (rejection-sampling approach inspired by Lemire’s algorithm), ensuring every symbol in the 62-character charset is equally likely.

CLI Example

$ coder provisioner keys create my-key
Successfully created provisioner key "my-key"! Save this authentication
token, it will not be shown again.
Psdlt0tkyPOSSsQ77G5WTSwH4zXY8JQFisadNNU3BJV (This is an example key)

Note: The key is shown once. Treat it like a password. If lost, create a new key (the original cannot be recovered).

This satisfies the theory laid down above.