Files
mizan/docs/MWT_SPEC.md
Ryth Azhur 6eca514777 Restore documentation layer — match current substrate
ROADMAP: done items moved out of "Next" (codegen rewrite, SSR bridge,
edge manifest, X-Mizan-Invalidate, return-type branching, affects_params,
kernel extraction, two-stage codegen, mizan-ts). Real "Next" in:
framework-adapter wrapper layer (MizanContext + useMizan + DjangoError
on top of the kernel) for React/Vue/Svelte; A1–A4 from ISSUES.md.

CLAUDE: 4-package layout replaced with the actual 7-package layered
architecture (backend protocol adapters + frontend kernel + framework
adapters + SSR worker). "STALE codegen" section rewritten to describe
what's emitted vs. the wrapper layer that isn't yet.

docs/ now tracked (6 files). AFI_ARCHITECTURE rewritten — replaced
the speculative `mizan-ast`/`mizan-csr`/`mizan-rpc`/`mizan-schema`
package names with the real layout, dropped KDL-schema language for
the actual schema-export format. The other 5 docs/ files were already
current and are tracked as-is.

ARCHITECTURE-REWORK.md deleted — same expert review is re-tracked in
the fresher ISSUES.md, two parallel trackers was sediment.

README.md deleted — drift was beyond surgical fixes (`mizan_clients.py`
convention, `<DjangoContext>` provider, removed `@compose` and
`context='local'`, wrong codegen output filenames, 3-package structure
vs. 7). Rewrite waits for the wrapper-layer codegen to land so
user-facing examples reflect reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 20:10:12 -04:00

60 lines
1.9 KiB
Markdown

# MWT — Mizan Web Token
*Decided 2026-04-06.*
MWT is a standard JWT (RFC 7519, HMAC-SHA256) with Mizan-specific
claims, traveling on the `X-Mizan-Token` header. It is the
protocol's identity layer for cache keying and permission staleness
detection.
## Claims
| Claim | Purpose |
|---|---|
| `sub` | User ID — goes into HMAC cache key derivation |
| `pkey` | Deterministic hash of user's permission state at issuance |
| `exp` | Configurable short TTL — controls permission staleness window (Django setting) |
| `iat` | Issued at |
| `kid` | Key ID — for secret rotation |
| `aud` | Audience binding — prevents cross-tenant replay |
## Key decisions
- **Standard JWT envelope, not proprietary.** Uses standard libraries
for signing and validation.
- **`X-Mizan-Token` header, not `Authorization: Bearer`.** Avoids
collision with DRF, allauth, and existing JWT systems. Cloudflare
WAF/Access do not inspect custom headers.
- **Replaces `JWTUser` + `_try_jwt_auth` entirely.** Old approach is
deleted.
- **App handles authentication** (session, social, etc.). Mizan
issues MWT *from* the authenticated identity.
- **Edge Worker** validates MWT, extracts `sub` for HMAC cache key,
checks `exp`.
- **`pkey` computation must be deterministic:**
`sorted(user.get_all_permissions())` then hash.
- **Client-side: proactive refresh before expiry.** Check TTL before
dispatch, not reactively after a 401.
- **Header-based, not cookie-based.** A cookie would force
`Vary: Cookie`, destroying PSR cache.
## HMAC canonical form
JSON with sorted keys:
```
HMAC(secret, JSON.stringify({"c": context, "p": sorted_params, "u": user_id}))
```
## What this solves
- DRF token collision
- `JWTUser`-too-thin problem
- Permission staleness race condition
- Single validation path across Python and TypeScript Edge
## Usage rule
All cache-layer auth code uses MWT, not Django session or raw JWT.
The `@client(auth=...)` parameter gates on MWT validity.