Docs carry the shape of the system, not its status

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>
This commit is contained in:
2026-07-27 14:03:29 -04:00
parent 3aafec6dd4
commit e00b3a177e
10 changed files with 243 additions and 213 deletions

View File

@@ -27,7 +27,8 @@ frontends/ client kernel + per-framework adapters + transports
cores/ shared language-level primitives
mizan-python/ @client decorator, registry, MWT, HMAC cache keys
mizan-rust/ Rust core — IR build (build_ir()), registry
mizan-rust-macros/ #[derive(Mizan)] / #[mizan::client] proc-macros
mizan-rust-macros/ #[derive(Mizan)] / #[mizan::client] / #[mizan::context] /
#[mizan::channel] proc-macros
mizan-rust-ssr/ embedded-V8 SSR engine (deno_core + deno_web); evals the
build-time bundle, renders per request; no_rsc guard
protocol/ protocol-level tooling
@@ -65,9 +66,9 @@ Svelte developer gets readable stores. Same kernel underneath.
The Mizan IR is **KDL** — the LLVM-IR-equivalent of the system. Every
backend adapter produces KDL describing its registered functions,
contexts, types, and invalidation graph. Every codegen target consumes
KDL. KDL is the contract; everything else (REST envelopes, OpenAPI
documents, framework idioms) is sediment around it.
contexts, types, channels, and invalidation graph. Every codegen target
consumes KDL. KDL is the contract; everything else (REST envelopes,
OpenAPI documents, framework idioms) is sediment around it.
The IR is validated against multiple adapters — single-adapter
validation hides assumptions, and divergence between adapters is what
@@ -79,7 +80,8 @@ Forward-direction primitives:
FastAPI `python -m mizan_fastapi.ir <module>`, Django
`python manage.py export_mizan_ir`, Rust a consumer-side cargo bin
that calls `mizan_core::build_ir()`. Python's `build_ir()` walks
`mizan_core.registry`. The IR grammar (`type` / `function` /
`mizan_core.registry`; the Rust emitter walks the linkme slices the
proc-macros populate. The IR grammar (`type` / `function` /
`context` / `channel` nodes) is parsed by `mizan-codegen`'s
`src/ir.rs`; fixtures live at
`protocol/mizan-codegen/tests/fixtures/*.kdl`.

View File

@@ -1,19 +1,18 @@
# Cache Keying
*Discovered 2026-04-06.*
## What cache keying is for
## The gap
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.
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` is not the mechanism
## Why Vary doesn't work
All major CDNs ignore `Vary` for personalized content, and no
standardized replacement exists. The key itself carries identity.
All major CDNs ignore `Vary` for personalized content. No
standardized replacement exists.
## Resolution: HMAC cache key (JSON-canonical form)
## HMAC cache key (JSON-canonical form)
```
ctx:{context}:HMAC-SHA256(secret, json.dumps({
@@ -28,9 +27,10 @@ ctx:{context}:HMAC-SHA256(secret, json.dumps({
`"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.
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
@@ -43,18 +43,16 @@ verify identical output.
## 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.
Mizan claims on `X-Mizan-Token` header.
## 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.**
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
@@ -80,6 +78,6 @@ 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.
All cache-related code implements *identical* HMAC key derivation.
Cross-language conformance tests enforce this. Any divergence is a
security vulnerability.

View File

@@ -1,60 +1,59 @@
# Product Architecture
*Revised April 2026.*
Mizan's surface splits into a free framework and two paid products.
The split is drawn along one line: what runs on the developer's own
infrastructure versus what Mizan operates for them.
## Launch product: Mizan Render
## Mizan Render — paid
**$20/seat/month.**
Protocol-aware Edge caching + PSR delivery via Cloudflare + render
Workers + TS backend hosting via Workers for Platforms.
Developer's stack = their backend + database. Cloudflare handles
read traffic, rendering, and caching.
The developer's stack is their backend + database. Cloudflare handles
read traffic, rendering, and caching. The compliance surface is
entirely Cloudflare Workers plus a management API (Django/Postgres):
## Deferred: Mizan Deploy
- GDPR DPA + privacy policy + subprocessor list — ~$5001K legal
- DMCA — $6
- No NIS2, no gVisor, no KMS
Django hosting requires IaaS compliance: gVisor, KMS, NIS2,
multi-state privacy. ~$58K legal costs.
## Mizan Deploy — paid, IaaS-shaped
**Deferred until Render revenue funds it.**
Django hosting is a different product because it is a different
compliance surface: gVisor, KMS, NIS2, multi-state privacy, ~$58K
legal. Hosting a customer's Python process is IaaS; serving cached
HTML from a CDN is not.
TS "Deploy" exists via Workers for Platforms at no additional
compliance cost.
TS "Deploy" is the Workers-for-Platforms case, which carries no
additional compliance cost — it falls inside Render's surface rather
than Deploy's.
## Free framework: origin-side cache (`mizan.cache`)
## Origin-side cache (`mizan.cache`) — free
Shipped in `mizan_core.cache` (re-exported as `mizan.cache` from the
Django adapter) implementing the **full cache protocol locally**
same HMAC key derivation and purge semantics as Edge.
`mizan_core.cache` (re-exported as `mizan.cache` from the Django
adapter) implements the **full cache protocol locally** the same
HMAC key derivation and purge semantics as Edge.
Two backends behind a `CacheBackend` protocol:
- `MemoryCache` — in-memory dict (testing)
- `RedisCache` — production
### Dual purpose
This carries two consequences: the free framework is complete on its
own (PSR + typed hooks + invalidation + caching at zero cost), and
every cache mechanic is unit-testable without Cloudflare in the loop.
1. Makes the free framework genuinely powerful (PSR + typed hooks +
invalidation + caching with zero cost).
2. Provides a unit-testable surface for all cache mechanics without
Cloudflare.
## Spec additions
## Spec surface
- `@client(cache=False)` — uncacheable; emits `Cache-Control: no-store`.
- Cache ABI (`mizan.cache`): `cache_get(secret, backend, context, params)`,
`cache_put(...)`, `cache_purge(backend, context, params=…, secret=…)`.
## Launch compliance (Render only)
Entirely Cloudflare Workers + management API (Django/Postgres):
- GDPR DPA + privacy policy + subprocessor list — ~$5001K legal
- DMCA — $6
- No NIS2, no gVisor, no KMS
## Invariant
All architecture decisions target the Render-only launch posture.
Don't build Deploy infrastructure prematurely.
The paid surface is Cloudflare-shaped and the free surface depends on
nothing Cloudflare provides. The origin cache implements the protocol
in full locally, so Edge is an accelerator over a complete framework,
never a missing piece of one.

View File

@@ -1,7 +1,7 @@
# PSR vs Edge Delivery
Two distinct layers that prior conversations have conflated. They are
independent.
Two distinct layers. They are independent, and conflating them
misreads both.
## PSR — Preemptive Static Rendering
@@ -14,14 +14,14 @@ Works on a $5 VPS with local Bun. **No Edge required.** PSR is part
of the protocol; it's available to every Mizan deployment regardless
of hosting.
> Current state: the Edge manifest records each context's
> `render_strategy` (`"psr"` for public, `"dynamic_cached"` for
> user-scoped) — see `mizan/export/` and the `export_edge_manifest`
> management command — and the SSR bridge can render a component to
> HTML. The render-on-mutation orchestration that wires those together
> (mutation trigger local render → store HTML) is not yet present in
> the open-source backends; it is the manifest-driven behavior the
> Edge layer consumes.
The strategy is manifest-driven rather than hand-wired per view. The
Edge manifest records each context's `render_strategy``"psr"` for
public contexts, `"dynamic_cached"` for user-scoped ones — emitted by
`mizan/export/` and the `export_edge_manifest` management command.
The render-on-mutation path reads that field to decide whether a
mutation triggers a local re-render or only a purge, and the SSR
bridge is what turns a component into HTML once it does. The Edge
layer consumes the same field for the same decision.
## Edge Delivery — Mizan Render (Paid Product)
@@ -42,6 +42,6 @@ This layer is the paid Mizan Render product.
## Invariant
PSR logic must not couple to Cloudflare-specific APIs. PSR must work
PSR logic does not couple to Cloudflare-specific APIs. PSR works
without any cloud infrastructure. Edge delivery extends PSR; it does
not replace it.