Files
storage/docs/architecture/decisions/050-sha256-for-api-key-hashing.md
glm-5.1 ae242f33b9 Restructure identity tables: separate credential types, add peer_credentials, specify FK cascades and indexes
Identity tables were derived from hub's PostgreSQL schema but simplified
without documenting what was removed or why. This restructures them for the
current auth landscape (API key + wraith SSH/cert-authority):

- ADR-049: Separate api_keys and peer_credentials tables (different lookup
  patterns, columns, lifecycles), remove Gitea columns, map hub data→metadata
- ADR-050: Extract SHA-256 vs KDF decision from inline spec text
- Add peer_credentials table for SSH key and cert-authority auth
- Specify all FK cascade behaviors within system DB (RESTRICT, CASCADE, SET NULL)
- Complete index specifications for all identity tables
- Add scope boundary section (storage owns schemas, not auth/authorization)
- Update audit_logs with credentialId+credentialType polymorphic reference
- Add 3 new open questions (OQ-33/34/35) for credential type expansion
2026-06-02 12:33:20 +00:00

52 lines
1.9 KiB
Markdown

# ADR-050: SHA-256 for Machine-Generated API Keys
## Status
Accepted
## Context
API key hashing has two common approaches:
1. **Fast hash** (SHA-256): O(1) verification at high throughput. Standard for
machine-generated tokens.
2. **Slow KDF** (bcrypt, Argon2): Intentionally expensive to slow brute-force
attacks. Standard for human-chosen passwords.
The choice depends on the input entropy. Human passwords are low-entropy
(maybe 30-40 bits of actual randomness even with complexity requirements), so
brute-force is feasible unless the hash is slow. Machine-generated keys are
high-entropy (128-bit+ randomness from `crypto.randomUUID()` or equivalent),
making brute-force computationally infeasible even with a fast hash.
## Decision
Use SHA-256 for API key hashing. Do not use bcrypt or Argon2.
The API keys in `@alkdev/storage` are machine-generated secrets with 128-bit+
entropy. An attacker attempting to brute-force a SHA-256 hash of such a key
faces 2^128 possible inputs — infeasible regardless of hash speed. Slow KDFs
add latency (50-200ms per verification) without meaningful security
improvement for high-entropy inputs.
## Consequences
**Positive:**
- Fast O(1) verification — critical for high-throughput API authentication
- Widely supported — every language/runtime has SHA-256 built in
- Simple implementation — no salt generation, no cost parameter tuning
**Negative:**
- If a consumer generates low-entropy keys (short, predictable patterns),
SHA-256 provides less protection against brute-force than a slow KDF. This
is a consumer responsibility — the storage table schema cannot enforce key
generation quality.
- SHA-256 is not post-quantum resistant. This is acceptable for API keys,
which can be rotated, unlike passwords which are often long-lived.
## References
- `api_keys.keyHash` in [sqlite-host.md](../sqlite-host.md)
- Hub ADR-010: SHA-256 for API key hashing (same decision, provenance)