ioredis cache-aside helpers, Cache-Control middleware, and AES key rotation

Dshamir adds three independent backend-ops primitives: a Redis client with cache-aside helpers, HTTP Cache-Control middleware, and an encryption key rotation module for stored user API keys.

securityinfrastructure

The Redis module (redis.ts) wraps ioredis with a lazy-connect client and three thin helpers: cacheGet(key), cacheSet(key, value, ttlSeconds), and cacheDelete(key). No cache-aside logic is applied to any route yet - this is infrastructure ahead of the planned endpoint-level caching work noted in the roadmap.

The HTTP cache middleware (middleware/cache.ts) adds two functions: setCacheHeaders(maxAge) returns a middleware that sets Cache-Control: private, max-age=N, and noCache sets Cache-Control: no-store. Twelve lines total. Again, nothing calls these yet; they are available for wiring to specific routes.

Key rotation (keyRotation.ts) is more immediately useful. It reads USER_API_KEYS_ENCRYPTION_SECRET and USER_API_KEYS_ENCRYPTION_SECRET_V2 from the environment, derives a 32-byte key from each via SHA-256, and exposes tryDecrypt() which tries all keys in reverse order (newest first). The idea is that you can add _V2 when rotating, re-encrypt stored rows under the new key without downtime, then remove the old secret. The userApiKeys.ts module was updated to delegate to this module. A follow-up in the security hardening work upgrades this further: SHA-256 key derivation is replaced with HKDF with a random 16-byte per-row salt stored in a new salt column.

So what The key rotation module is the highest-value piece here, especially if you store user-provided API keys at rest. The Redis client and Cache-Control middleware are clean building blocks that take minutes to import. None of these three modules has dependencies on the Prisma migration or the Docker stack.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

3 commits from Dshamir/AI-Legal, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
6363aee8 [P6] feat: add Redis client with cache-aside helpers Dshamir 2026-05-23 ↗ GitHub
Install ioredis and create redis.ts with lazy-connect client,
cacheGet/cacheSet/cacheDelete helpers for cache-aside pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
af5de5bb [P6] feat: add HTTP cache header middleware Dshamir 2026-05-23 ↗ GitHub
setCacheHeaders(maxAge) and noCache middleware for controlling
Cache-Control headers on API responses.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
acdfec42 [P6] feat: add encryption key rotation support Dshamir 2026-05-23 ↗ GitHub
commit body
Add keyRotation.ts with multi-key tryDecrypt that attempts all
configured secrets (USER_API_KEYS_ENCRYPTION_SECRET, _V2).
Update userApiKeys.ts to delegate encryption/decryption to the
rotation module for seamless key migration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-522.md from inside the repo you want the changes in.

⬇ Download capture-thread-522.md