The crypto/password package provides a KDF-agnostic Encoder interface and a DelegatingEncoder that tags each stored hash with an {id} algorithm prefix, so multiple algorithms can coexist in the same password column.

Import

Encoder interface

Matches must run in constant time to resist timing attacks.

DelegatingEncoder

Routes encode/verify calls to the right algorithm based on the {id} prefix.
Pre-registered delegates:

Legacy unprefixed hashes

For a column with unprefixed bcrypt hashes (predating the {id} prefix convention):
Old rows verify with the fallback; new writes get the {bcrypt} prefix. No re-hashing migration job required.

Registering a custom encoder

BcryptEncoder

DefaultBcryptCost is 10 — a widely-used baseline that balances hashing time against brute-force resistance. A cost outside bcrypt’s valid range falls back to the default.

Argon2idEncoder

Default parameters: m=65536 (64 MiB), t=3 iterations, p=2 threads. Override any of them with functional options:
Out-of-range values are clamped to RFC 9106’s floor and a resource ceiling rather than erroring, so a misconfigured option can’t silently produce a weak hash or an accidental denial-of-service.

NoopEncoder

Stores and compares passwords as plain text. Tests and local development only — never use it in production.

Choosing an algorithm

Encoding a new password

Store hash in your users.password_hash column.