Copy architecture docs, ADRs, storage domain specs, research, reviews, and 56 storage architecture tasks from the alkhub_ts monorepo. Adapt for standalone @alkdev/hub repo structure (src/ not packages/hub/). Sanitize all sensitive information: - Replace private IPs (10.0.0.1) with localhost defaults - Remove internal server hostnames (dev1, ns528096) - Replace /workspace/ private paths with npm package references - Remove hardcoded credentials from examples - Rewrite infrastructure.md without private network details Add Deno project scaffolding: deno.json (pinned deps), .gitignore, AGENTS.md, entry point. Migrate existing code stubs (crypto, config types, logger) with updated import paths.
1.7 KiB
ADR-010: API keys vs client secrets — direction matters
- Status: Accepted
- Date: 2026-04-19
- Deciders: alkdev
Context
Both api_keys and client_secrets store authentication credentials, but they serve opposite directions.
Decision
Keep as separate tables with different security models. api_keys: keys WE issue so others can call US (hub auth). Managed by keypal. Stored as SHA-256 hashes. client_secrets: keys OTHERS issue so we can call THEM (outbound auth). Managed by us. Stored as AES-256-GCM encrypted values. Never mix — a hashed client secret is useless (we can't send it), an encryptable API key defeats the purpose of hashing.
SHA-256 vs KDF trade-off
API keys are hashed with SHA-256, not a deliberately slow KDF (bcrypt, Argon2). This is acceptable because:
- API keys are high-entropy machine-generated strings (128-bit+). With 2^128 key space, brute-force is infeasible regardless of hash speed — there are not enough keys to make a dictionary attack viable.
- SHA-256 provides O(1) verification latency at high throughput, which matters for every API request.
- Slow KDFs exist to protect low-entropy human passwords (where rate-limiting cannot compensate for small key space). Machine-generated keys do not have this weakness.
If the database is compromised, the attacker has the SHA-256 hashes but cannot reverse them without enumerating the key space — which is computationally infeasible for 128-bit+ random keys.
Consequences
Positive: clear security model per direction, appropriate crypto per use case, no confusion about how credentials are stored. Negative: two tables instead of one, but the security models are fundamentally incompatible so merging would be wrong.