meridian
The UI as a proto — one descriptor, rendered natively on web, React, and terminal.
A server-driven UI framework where an interface is a protobuf message rather than frontend code. One 21-arm PanelDescriptor is authored once and drawn by independent web, React/MUI, and Rust/ratatui renderers, with a shared Rust core compiled to wasm so ranking and validation cannot fork between surfaces.</parameter> <parameter name="story">meridian treats a user interface as data: a single `meridian.ui.v1.PanelDescriptor` — a 21-arm oneof spanning tables, forms, galleries, command palettes, charts, KPI tiles, interactive terminals and append-only log streams — authored once and rendered by surfaces that know nothing about each other. Style, semantics and rendering are deliberately orthogonal proto layers, and the dependency rule runs one way: emitters and renderers depend on the schema module, nothing depends on a renderer. That constraint is what allows a Rust interaction core to be shared verbatim between the browser and a ratatui terminal renderer, compiled to wasm for the former and linked natively for the latter, crossing the boundary as protobuf binary so ranking, validation and dedupe cannot diverge between surfaces. Each panel is documented with a degradation ladder rather than a web-only rendering, so a surface that cannot draw a video shows a poster and then alt text. Two pieces were hard-won. Renderer coverage used to be "only knowable by reading six dispatch sites in four repositories and three languages", so it became a machine-checked manifest — a 21-shape by 6-renderer matrix where every non-rendering cell carries a written reason and adding a proto arm without declaring coverage fails the build. And because the MCP server lets a model author a descriptor, the admission gate derives read-versus-mutation from the renderer callsite rather than from any field on the descriptor, precisely so an untrusted author cannot promote a read into a mutation. The project's most consequential event was folding ten repositories into one workspace: 279 commits preserved, five pairwise-disjoint versions of the generated proto types collapsed to one, and CI added to four repos covering some 14,500 lines that had none — which immediately surfaced a `Block.view` arm that had gone unhandled for four minor releases because nobody could build the TUI.