# 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)

```
HMAC-SHA256(secret, JSON.stringify({
    "c": context,
    "p": sorted_params,
    "r": rev,
    "u": user_id    // omitted for public content
}, sort_keys=True))
```

### 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. 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, backed by Redis.
**Conformance verified by a shared test suite.**

### Required operations

- `cache_get`
- `cache_put`
- `cache_purge`
- `cache_purge_user`

### Storage

Redis only. Handles persistence, cross-worker sharing, crash
recovery.

## 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.
