Files
mizan/README.md

144 lines
7.2 KiB
Markdown

# Mizan
Mizan is an Application Framework Interface (AFI). A single `@client` decorator on a
server function generates a typed frontend client; cache invalidation and caching are
handled by the protocol.
```python
from mizan import client, ReactContext
UserContext = ReactContext('user')
# Context function — bundled into GET /api/mizan/ctx/user/
@client(context=UserContext)
def user_profile(request, user_id: int) -> UserShape:
return UserShape.query(lambda qs: qs.filter(pk=user_id))[0]
# Mutation — invalidation scoped automatically by matching param name
@client(affects=UserContext)
def update_profile(request, user_id: int, name: str) -> dict:
...
```
Adapters exist for Django, FastAPI, Rust/Axum, Tauri, and TypeScript. Django is the
reference implementation; per-adapter support is inventoried below.
> **Status:** Mizan is not production-tested. It passes its own test suites but has not
> been run in a production deployment. Treat it as pre-release.
## Documentation
- [`docs/`](docs/) — architecture references: AFI, SSR, cache keying, MWT, PSR vs. Edge
- [`ROADMAP.md`](ROADMAP.md) · [`ISSUES.md`](ISSUES.md) — planned work and known gaps
## Backend adapters
Every adapter implements the same AFI wire protocol. The matrix below inventories
support per adapter, grouped to separate protocol guarantees from Django-specific
features (forms, ORM projection, auth providers, SSR). A cell counts as supported only
when that adapter wires the capability into its own dispatch surface, not merely that a
shared core primitive exists.
Legend: ✅ supported · ◑ partial · ❌ not implemented · — not applicable to this transport
### Protocol core
The surface every Mizan adapter implements.
| Capability | Django | FastAPI | Rust / Axum | Tauri | TypeScript |
|---|:---:|:---:|:---:|:---:|:---:|
| RPC call dispatch (`{result, invalidate}`) | ✅ | ✅ | ✅ | ✅ ¹ | ✅ |
| Named-context bundle fetch | ✅ | ✅ | ✅ | ✅ | ✅ |
| Invalidation — JSON body | ✅ | ✅ | ✅ | ✅ | ✅ |
| Invalidation auto-scoping (three-tier) | ✅ | ✅ | ✅ | ✅ | ✅ |
| Function discovery / registration | ✅ | ✅ | ✅ | ✅ | ✅ |
| Codegen IR export (KDL) | ✅ | ✅ | ✅ ⁶ | ✅ ⁶ | ❌ ⁸ |
| File uploads (multipart, `Upload` type) | ✅ | ✅ | ❌ ⁹ | ❌ ⁹ | — ¹⁰ |
### Edge, cache & enforcement
Protocol transports and guarantees co-equal with the body channel in the spec.
| Capability | Django | FastAPI | Rust / Axum | Tauri | TypeScript |
|---|:---:|:---:|:---:|:---:|:---:|
| Invalidation — `X-Mizan-Invalidate` header | ✅ | ✅ | ❌ | — ¹ | ✅ |
| Auth-guard enforcement (`auth=…` rejects) | ✅ | ✅ | ❌ ⁵ | ◑ ⁵ | ✅ ¹¹ |
| Origin-side HMAC cache | ✅ | ✅ | ❌ | ❌ | ✅ |
| Edge manifest export | ✅ | ❌ | ❌ | — | ✅ |
| PSR (`render_strategy` in manifest) | ✅ | ❌ | ❌ | — | ✅ |
| Session / CSRF init endpoint | ✅ | ◑ ⁷ | ◑ ⁷ | — | ❌ |
> **Caveat:** Rust/Axum and Tauri accept `auth=` on a function but do not yet enforce
> it — do not rely on `auth=` for access control on those adapters.
>
> Django, FastAPI, and TypeScript share one auth/invalidation/cache implementation
> (`mizan_core` for the Python adapters; the same spec, pinned cross-language, for TS).
### Stack extensions (Django)
Django ecosystem features Mizan wraps. Other adapters provide these only where the
target stack calls for them.
| Capability | Django | FastAPI | Rust / Axum | Tauri | TypeScript |
|---|:---:|:---:|:---:|:---:|:---:|
| WebSocket channels (declared transport) | ✅ | ❌ | ◑ ² | ❌ | ❌ |
| Forms (schema / validate / submit) | ✅ | ❌ | ◑ ³ | ❌ | ❌ |
| Formsets | ✅ | ❌ | ❌ | ❌ | ❌ |
| API shapes (ORM query projection) ⁴ | ✅ | — | — | — | — |
| JWT auth (access / refresh) ¹² | ✅ | ✅ | ❌ | ❌ | ◑ ¹³ |
| MWT (edge identity token) | ✅ | ✅ | ❌ | — | ◑ ¹³ |
| SSR bridge | ✅ | ❌ | ❌ | — | ❌ |
| Auth-provider integration (allauth) | ✅ | ❌ | ❌ | ❌ | ❌ |
**Notes**
1. Tauri's transport is Tauri IPC (a single `#[tauri::command]` envelope), not HTTP.
Invalidation rides in the JSON response body; there is no header channel.
2. Rust/Axum declares `Transport::Websocket` in the IR/macro but routes no Axum
WebSocket handler yet.
3. Rust/Axum carries `is_form`/`form_role` trait stubs but no validate/submit endpoint.
4. "API shapes" is Django's django-readers queryset projection — ORM-coupled. Every
adapter carries typed input/output through the KDL IR; the projection primitive
itself is Django-only.
5. Tauri's `FunctionSpec` carries `auth`/`private` fields; the dispatch path does not
enforce them. Rust/Axum has no enforcement either.
6. Rust/Axum and Tauri are the IR authority via the `#[mizan::client]` macro + linkme
registry; the codegen links the crate directly (`build_ir()` / the `export-ir` bin)
rather than fetching over HTTP.
7. FastAPI and Rust/Axum expose `GET /session/` returning a null CSRF token for wire
parity; CSRF is Django-only.
8. `mizan-ts` emits the Edge manifest (JSON) but has no KDL IR emitter, so it can't yet
feed the codegen — an unbuilt gap. A TypeScript backend still needs the generated
client (types + `callXxx`/`fetchXxx` + framework hooks); same language doesn't remove
the need for it.
9. The `mizan-codegen` crate parses the `upload` KDL node and emits the field across
targets (the Rust target lowers it to `Vec<u8>`). Multipart dispatch binding is wired
for Django and FastAPI only; the Rust/Axum and Tauri *adapters* have no upload concept
at dispatch yet.
10. The TypeScript column is the `mizan-ts` backend adapter, which has no upload
dispatch. The matching client side lives in the kernel (`@mizan/base`): `mizanCall`
auto-switches to `multipart/form-data` when any argument is a `File`.
11. `mizan-ts` dispatch now enforces `auth=` (`true`/`'staff'`/`'superuser'`/predicate)
against a host-supplied `Identity`, byte-matching the Python guard's denial messages.
12. JWT/MWT token logic is single-sourced in `mizan_core.auth`; Django and FastAPI ride
it. Session-validation (immediate-logout revocation) is Django-only — FastAPI mints
from its own credential check.
13. `mizan-ts` ships an optional `decodeMwt`/`decodeJwtBearer`/`identityFromMwt` helper
(HS256 via Node `crypto`, cross-language pin-tested against a Python-minted MWT) so a
TS edge worker can derive `Identity` from a Python-issued token. Identity source stays
host-supplied; `mizan-ts` does not mint from a session.
## Conformance
Adapter parity is gated by the AFI conformance suite in [`tests/afi/`](tests/afi/). It
currently asserts **IR-shape parity** — the same fixture through Django, FastAPI, and
the Rust adapter emits byte-identical KDL (`test_codegen_parity.py`). Per-capability
runtime assertions (header transport, `auth=` enforcement, cache behavior) are planned.
## License
Mizan is licensed under the [Elastic License 2.0](LICENSE) (SPDX: `Elastic-2.0`). You
may use, copy, modify, and distribute it freely, including in commercial products you
build on top of it. You may **not** provide Mizan to third parties as a hosted or
managed service that exposes a substantial set of its features.