Files
mizan/docs/CACHE_KEYING.md

2.7 KiB

Cache Keying

Discovered 2026-04-06.

The gap

Mizan specified invalidation but never specified cache keying. Without correct cache keying, Edge caching is a security vulnerability — it serves User A's content to User B.

Why Vary doesn't work

All major CDNs ignore Vary for personalized content. No standardized replacement exists.

Resolution: HMAC cache key (JSON-canonical form)

ctx:{context}:HMAC-SHA256(secret, json.dumps({
    "c": context,
    "p": sorted_params,   // values normalized to JSON-native strings
    "r": rev,
    "u": user_id          // omitted for public content
}, sort_keys=True, separators=(",", ":")))

derive_cache_key(secret, context, params, user_id=None, rev=0)"ctx:{context}:{hmac_hex}". The ctx:{context}: prefix lets broad purge SCAN by prefix. Param values are normalized for cross-language consistency (True"true", None"null") before stringification. Implemented in cores/mizan-python/src/mizan_core/cache/keys.py and backends/mizan-ts/src/cache/keys.ts (deriveCacheKey); pin tests verify identical output.

Key derivation rules

  • Public content — URL path + query params (standard CDN).
  • User-scoped content — HMAC key derivation above.
  • @client(auth=...) determines whether content is user-scoped.
  • rev parameter on @client for deploy-time logic invalidation. Bumped by the developer when function logic changes.

Identity layer

MWT (Mizan Web Token) — see MWT_SPEC.md. JWT with Mizan claims on X-Mizan-Token header. Replaces the old JWTUser + permission key metadata approach.

Cache architecture

Decided 2026-04-06.

Not a compiled binary ABI. Not a pluggable Python protocol.

Each backend adapter (Python, TypeScript, future PHP/C#/Go) implements the cache protocol in its own language. Conformance verified by a shared test suite.

Required operations

  • cache_get
  • cache_put
  • cache_purge (scoped recomputes the key; broad SCANs the ctx:{context}:* prefix)

Storage

Two backends behind a CacheBackend protocol (mizan_core/cache/backend.py):

  • MemoryCache — dict-based, for testing.
  • RedisCache — production; persistence, cross-worker sharing, crash recovery. Broad purge via SCAN, delete via UNLINK.

Deploy invalidation

No full context flush. The rev parameter on @client is part of the HMAC key. When the developer bumps rev, old cache entries become unreachable orphans. No purge needed; no thundering herd.

Invariant

All cache-related code must implement identical HMAC key derivation. Cross-language conformance tests enforce this. Any divergence is a security vulnerability.