Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Zozi96/hash-forge/llms.txt

Use this file to discover all available pages before exploring further.

HashManager is the central orchestrator in Hash Forge. It holds one or more hasher instances, routes verification to the correct algorithm automatically, and exposes a consistent API for hashing, verifying, migrating, and inspecting password hashes regardless of which algorithm produced them. Every feature in the library — async support, the builder pattern, configuration loading, and password policies — is ultimately surfaced through HashManager.

Creating a HashManager

There are four ways to obtain a HashManager instance. Choose the one that fits how your application manages configuration.

String-based factory

HashManager.from_algorithms() is the quickest way to get started. Pass one or more algorithm name strings and the factory creates the appropriate hasher objects for you using default parameters.
from hash_forge import HashManager

# Single algorithm
manager = HashManager.from_algorithms("pbkdf2_sha256")

# Multi-algorithm — first entry is the preferred (used for new hashes)
manager = HashManager.from_algorithms("pbkdf2_sha256", "bcrypt")

Direct instantiation

Construct hashers yourself for full control over every parameter, then pass them directly to HashManager.
from hash_forge import HashManager
from hash_forge.hashers import Argon2Hasher, BCryptHasher

manager = HashManager(
    Argon2Hasher(time_cost=4, memory_cost=65536),  # preferred
    BCryptHasher(rounds=12),                        # fallback
)

Config-driven

HashManager.from_config() reads algorithm parameters from a HashForgeConfig object — loaded from environment variables, a JSON file, or a dict — and wires up the hashers for you.
from hash_forge import HashManager
from hash_forge.config import HashForgeConfig

config = HashForgeConfig.from_env()
manager = HashManager.from_config(config, "argon2", "pbkdf2_sha256")

Policy-driven

HashManager.from_policy() creates a manager that enforces a PasswordHashPolicy profile. The policy controls which algorithm is preferred, which are permitted for verification, and whether deprecated algorithms are blocked.
from hash_forge import HashManager, PasswordHashPolicy

manager = HashManager.from_policy(PasswordHashPolicy.recommended())

Core Operations

Hashing a password

hash() always delegates to the preferred hasher — the first hasher provided during construction.
manager = HashManager.from_algorithms("pbkdf2_sha256")

hashed = manager.hash("my_secure_password")
print(hashed)
# pbkdf2_sha256$150000$<salt>$<hash>

Verifying a password

verify() inspects the hash string to determine which algorithm produced it, dispatches to that hasher, and returns True or False. You never need to specify the algorithm manually.
is_valid = manager.verify("my_secure_password", hashed)
print(is_valid)  # True

is_valid = manager.verify("wrong_password", hashed)
print(is_valid)  # False
verify dispatches to the correct hasher using an O(1) prefix lookup. If no registered hasher recognises the hash prefix, it falls back to a sequential scan and returns False when no match is found — it never raises an exception on unknown input.

Checking if a rehash is needed

needs_rehash() returns True when the hash was produced with parameters that no longer match the current hasher configuration, or when the algorithm used is not the preferred one.
from hash_forge.hashers import PBKDF2Sha256Hasher

old_hasher = PBKDF2Sha256Hasher(iterations=150_000)
new_hasher = PBKDF2Sha256Hasher(iterations=200_000)

old_hash = old_hasher.hash("password")

manager = HashManager(new_hasher, old_hasher)
print(manager.needs_rehash(old_hash))  # True — iterations changed
The first hasher added to HashManager is always the preferred hasher. All calls to hash() use this hasher, and needs_rehash() returns True whenever the stored hash was not produced by the preferred hasher.

Advanced Operations

Rotate — verify then re-hash

rotate() combines verify() and hash() into a single call. If verification succeeds it immediately re-hashes with the preferred hasher and returns the new hash. If verification fails it returns None — no exception, no exposure of plaintext to the caller.
manager = HashManager.from_algorithms("argon2", "pbkdf2_sha256")

old_hash = get_stored_hash(user_id)           # e.g. a pbkdf2_sha256 hash
new_hash = manager.rotate(user_password, old_hash)

if new_hash is not None:
    save_hash(user_id, new_hash)              # now stored as argon2
    print("Password migrated to argon2")
else:
    print("Wrong password — not migrated")

Verify and update

verify_and_update() returns a two-element tuple: a bool indicating whether verification succeeded, and either a new hash string (if a rehash is required) or None (if the stored hash is already up to date).
is_valid, new_hash = manager.verify_and_update(user_password, stored_hash)

if not is_valid:
    raise PermissionError("Invalid password")

if new_hash is not None:
    save_to_db(new_hash)   # parameters or preferred algorithm changed
The return type is tuple[bool, str | None].

Inspect a hash

inspect() returns a dictionary of metadata about a stored hash without revealing the raw digest or salt. The dictionary always contains:
KeyTypeDescription
algorithmstrCanonical algorithm name
categorystrOne of password, legacy, digest, deprecated
deprecatedboolTrue if the algorithm is deprecated
Algorithm-specific parameters (e.g. iterations for PBKDF2, rounds for bcrypt) are included when available.
from hash_forge import HashManager
from hash_forge.hashers import PBKDF2Sha256Hasher, SHA3_256Hasher

manager = HashManager(PBKDF2Sha256Hasher(), SHA3_256Hasher())

pbkdf2_hash = HashManager.quick_hash("password", algorithm="pbkdf2_sha256", iterations=200_000)
print(manager.inspect(pbkdf2_hash))
# {'algorithm': 'pbkdf2_sha256', 'category': 'password', 'deprecated': False, 'iterations': 200000}

sha3_hash = HashManager.quick_hash("password", algorithm="sha3_256")
print(manager.inspect(sha3_hash))
# {'algorithm': 'sha3_256', 'category': 'digest', 'deprecated': False}

print(manager.inspect("unknown$abc$def"))
# None

List registered algorithms

list_algorithms() returns the algorithm names registered in the current manager instance, in insertion order.
manager = HashManager.from_algorithms("argon2", "pbkdf2_sha256", "bcrypt")
print(manager.list_algorithms())
# ['argon2', 'pbkdf2_sha256', 'bcrypt']

Quick Hash

HashManager.quick_hash() is a static method for one-off hashing without creating a persistent manager instance. It accepts an optional algorithm argument (defaults to "pbkdf2_sha256") and passes any remaining keyword arguments to the underlying hasher.
from hash_forge import HashManager

# Default algorithm
hashed = HashManager.quick_hash("simple_password")

# Specific algorithm with custom parameters
hashed = HashManager.quick_hash("password", algorithm="bcrypt", rounds=14)
hashed = HashManager.quick_hash("password", algorithm="argon2", time_cost=4)
hashed = HashManager.quick_hash(
    "password",
    algorithm="pbkdf2_sha256",
    iterations=200_000,
    salt_length=16,
)
quick_hash creates a single-use hasher instance and discards it after hashing — it is not suitable for verification workflows where you need to persist the manager.

Build docs developers (and LLMs) love