5.0 KiB
AFI Architecture
Mizan is an Application Framework Interface (AFI) — the server-client unification layer.
Package layout
Tree organized by role.
backends/ server protocol adapters
mizan-django/ Django adapter
mizan-fastapi/ FastAPI adapter (AFI-common scope)
mizan-rust-axum/ Rust/Axum adapter (handlers, errors, IR export)
mizan-tauri/ Tauri adapter — Mizan calls served in-process
mizan-ts/ TypeScript adapter (proves the protocol is language-agnostic)
frontends/ client kernel + per-framework adapters + transports
mizan-base/ framework-agnostic kernel (@mizan/base); owns data, status,
error; adapters subscribe through the MizanTransport interface
mizan-react/ React contexts + hooks over the kernel
mizan-vue/ Vue composables over the kernel
mizan-svelte/ Svelte stores/runes over the kernel
mizan-rust/ Rust client kernel
mizan-tauri-transport/ MizanTransport over Tauri IPC
mizan-webview-transport/ MizanTransport over a webview message channel
mizan-webview-channels/ channel transport over a webview bridge
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
protocol/ protocol-level tooling
mizan-codegen/ codegen — Rust binary (crate `mizan-codegen`); reads KDL IR,
emits typed clients. Targets: stage1, react, vue, svelte,
channels, python, rust. Askama templates under templates/.
mizan-generate/ thin npm-package launcher (bin/launcher.mjs) dispatching to
the compiled mizan-codegen binary per platform
workers/ runtime workers / bridges
mizan-ssr/ Bun subprocess used by the Django template backend
Two orthogonal products
- RPC — typed client generation via codegen
- SSR — server rendering via the Bun bridge
Independent and composable. Either ships standalone; together they compose.
Kernel model
The client kernel (@mizan/base) is the one hard thing. It owns
ContextState<T> = {data, status, error}, the context registry
(registerContext), mizanCall / mizanFetch, server-driven merge
and invalidate, and initSession. It reaches the backend through a
pluggable MizanTransport (call / fetch); the default is the
HTTP httpTransport(), swapped via configure({ transport }) for
Tauri / webview hosts. Per-framework adapters are thin idiomatic
wrappers that subscribe to the kernel. Codegen emits typed bindings
against the framework adapter's surface — a React developer gets
useEcho() hooks, a Vue developer gets useEcho() composables, a
Svelte developer gets readable stores. Same kernel underneath.
KDL is the IR
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.
The IR must be validated against multiple adapters before it is considered stable. Single-adapter validation hides assumptions — divergence between adapters is what the IR exists to prevent.
Forward-direction primitives:
- Each backend adapter emits KDL on stdout from an IR-export command:
FastAPI
python -m mizan_fastapi.ir <module>, Djangopython manage.py export_mizan_ir, Rust a consumer-side cargo bin that callsmizan_core::build_ir(). Python'sbuild_ir()walksmizan_core.registry. The IR grammar (type/function/context/channelnodes) is parsed bymizan-codegen'ssrc/ir.rs; fixtures live atprotocol/mizan-codegen/tests/fixtures/*.kdl. protocol/mizan-codegen/src/fetch.rsspawns the configured source command and parses the KDL it writes.- Codegen reads KDL directly — no OpenAPI envelope, no
openapi-typescript, no per-backend converter divergence. The former JavaScript/Node two-stage codegen (openapi-typescriptplus.mjsadapters) has been deleted; codegen is now the single Rust binary. - Edge manifest, MWT claims, and other protocol artifacts derive from the same registry/IR.
Launch surface
Python (Django) + React. Vue and Svelte ship as v1 alongside React.
TypeScript backend (mizan-ts) proves the protocol is portable.
Why the AFI shape
Quadratic ecosystem growth (N server adapters × M client adapters) collapses to linear (one adapter per stack) when both sides communicate through a shared protocol.
Invariants
- All cross-package communication goes through the protocol. No direct cross-package dependencies.
- New adapters land as new packages, not as modifications to existing ones.
- Framework adapters wrap the kernel in framework idioms — they don't bypass it. Codegen targets the adapter, not the raw kernel.