MIZAN.md's phase-numbered implementation order, PRODUCT_ARCHITECTURE's "Deferred until Render revenue funds it" and "Shipped in", PSR_VS_EDGE's current-state section, and the READMEs' passing-test counts were all reporting where the work stood rather than what the system is. OWED_SURFACE keeps its subject — surface that is specified but unbuilt — stated as the shape each unit owes. The channel sections follow the renamed slots: Params / ClientMessage / ServerMessage, and Channel as the base class on both backends. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
84 lines
2.7 KiB
Markdown
84 lines
2.7 KiB
Markdown
# Cache Keying
|
|
|
|
## What cache keying is for
|
|
|
|
Mizan's invalidation surface names *which* entries die. Cache keying
|
|
names *which entry is which*. A key that does not separate User A's
|
|
content from User B's turns Edge caching into a
|
|
**security vulnerability** — it serves one user's content to another.
|
|
|
|
## Why `Vary` is not the mechanism
|
|
|
|
All major CDNs ignore `Vary` for personalized content, and no
|
|
standardized replacement exists. The key itself carries identity.
|
|
|
|
## 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.
|
|
The derivation lives in
|
|
`cores/mizan-python/src/mizan_core/cache/keys.py` and
|
|
`backends/mizan-ts/src/cache/keys.ts` (`deriveCacheKey`); pinned
|
|
vectors hold the two outputs byte-identical.
|
|
|
|
### 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](MWT_SPEC.md). JWT with
|
|
Mizan claims on `X-Mizan-Token` header.
|
|
|
|
## Cache architecture
|
|
|
|
**Not a compiled binary ABI. Not a pluggable Python protocol.**
|
|
|
|
Each backend adapter (Python, TypeScript, PHP, C#, Go) implements the
|
|
cache protocol in its own language. **Conformance is verified by a
|
|
shared test suite**, so the implementations cannot drift apart
|
|
silently.
|
|
|
|
### 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 implements *identical* HMAC key derivation.
|
|
Cross-language conformance tests enforce this. Any divergence is a
|
|
security vulnerability.
|