# ADR-008: Secrets encrypted at rest with key versioning - **Status**: Accepted (revised 2026-04-23) - **Date**: 2026-04-19 - **Revised**: 2026-04-23 - **Deciders**: alkdev ## Context API keys, passwords, OAuth tokens, and SSH keys for external services must be stored securely. The crypto.ts utility from ade-v0 (AES-256-GCM + PBKDF2 with key version support) is battle-tested. The original decision specified reading the encryption key from an environment variable (`HUB_ENCRYPTION_KEY`). This is a security concern: environment variables are readable via `/proc/PID/environ` by any process with the same UID on the host, and are visible in `docker inspect`. In a multi-container Docker environment, this is a real attack surface. ## Decision Copy crypto.ts to packages/core/utils/crypto.ts. Store encrypted secrets in client_secrets.value as EncryptedData { keyVersion, salt, iv, data }. **Two-layer key model** (revised from original): 1. **Master key** — Provisioned via Docker secret (`/run/secrets/hub_master_key`). tmpfs-backed, never on container filesystem, not visible in `/proc/environ`. Used only to decrypt the config file's encrypted fields. Rarely rotated (requires redeploying the Docker secret). 2. **Data encryption keys** — Stored in the config file's `encryptionKeys` field (itself encrypted with the master key). Multi-key format: `v1:base64,v2:base64` — the first key is "current" (used for new encryptions), all keys are available for decryption (enables rotation). Generated via `crypto.generateEncryptionKey()`. Rotated by updating the config file and re-encrypting `client_secrets` rows — no Docker secret change needed. Key versioning supports rotation — bump keyVersion, re-encrypt on next access. The rotation protocol is defined in storage/services.md. **No environment variables for secrets or important configuration.** This is a hard rule. Non-sensitive convenience vars (e.g., `ALKHUB_CONFIG_PATH`) are acceptable. Nothing that would be damaging if exposed via `/proc` may be in an env var. Full config system specification: [docs/architecture/hub-config.md](../docs/architecture/hub-config.md). Startup sequence: [docs/architecture/hub-startup.md](../docs/architecture/hub-startup.md). ## Consequences Encryption keys must be available at runtime. If lost, all secrets unrecoverable. Standard for symmetric encryption. **Positive**: Key versioning enables rotation without downtime. Proven crypto implementation. Docker secrets eliminate the `/proc/environ` leak vector. Two-layer keys allow independent rotation schedules (master key rarely, data keys as needed). Config file with encrypted fields is safe to version-control (ciphertext only). **Negative**: Encryption key loss means total data loss (same as before). Two keys to manage instead of one. Slightly more complex deployment (mount config file + secret, rather than just setting env vars). Config file must be prepared with the `alkhub-config` CLI tool before deployment. **Mitigated by**: Storing master key in Docker secrets (not DB, not env), supporting key rotation so compromised keys can be cycled, `alkhub-config` tool automating config file preparation, infrastructure.md documenting the Docker deployment pattern.