ADR 0001: Separate Language, Protocol, and Runtime
- Status: Accepted
- Date: 2026-07-22
- Decision owners: Rixx UI maintainers
Context
Rixx UI must let language models create polished, interactive interfaces without allowing model output to execute code or bypass host policy. The primary product is a provider-neutral React response renderer. Progressive rendering and protocol evolution remain important implementation requirements; model hosting, chat sessions, and persistence belong to the host application. A single representation cannot optimize equally for model generation, network transport, durable storage, validation, and rendering. Deep JSON trees are awkward to stream and repair. A custom text language is compact for models but is a poor long-term interchange format. React elements are framework-specific and unsafe as an external contract.Decision
Rixx UI has three independent layers.- Rixx UI Language is the compact, line-oriented format generated by models.
- Rixx UI Protocol is the canonical stream of framed, versioned JSON records.
- The runtime stores a normalized graph and renders only host-registered components.
Language properties
- One complete statement per physical line.
- Stable assignment IDs and flat references rather than nested component trees.
- Named component arguments only.
- Forward references are legal within one transaction.
- Data, host-provided drafts, item scope, and host context use distinct reference roots.
- No raw HTML, JavaScript, CSS, generated SVG, network definitions, or
executable expressions. Sanitized Markdown is permitted only as an explicit
RichTextpayload. URL values never grant fetching or navigation permission. - A full authored document starts with
@ruiand ends with@commit. The normal model-facing fragment contains assignments only; a trusted host supplies the envelope and commit after provider completion and authoritative validation. - Model generation recovers at physical-line boundaries. Invalid statements are quarantined while the last safe provisional graph remains visible and inert.
Protocol properties
- JSON tuples minimize repeated envelope keys while retaining standard parsers.
- A transport supplies framing: one record per SSE event, WebSocket message, or RFC 7464 JSON text sequence.
- Sequence numbers are contiguous and transaction-local.
- Every transaction names its base revision and commits exactly one new revision.
- Graph changes become authoritative atomically at commit.
- An abort record is terminal and never changes the committed graph.
- Snapshots and resumable transport are internal/advanced concerns, not required by the public source-renderer API.
Runtime properties
- Components are stored by stable node ID.
- UI structure and protocol data live in the normalized snapshot. Context and draft values are read-only host inputs in the current renderer.
- Actions are declarative names emitted to the host; they contain no code or arbitrary network requests and Rixx UI does not execute them.
- Accessibility, focus management, responsive layout, and motion semantics are renderer responsibilities.
- Native declarative components render directly. Executable third-party apps require a separate-origin sandbox and are not part of the core language.
Alternatives rejected
Model-generated React, HTML, or JavaScript
Rejected because it creates an executable injection boundary, makes consistent design and accessibility difficult, and prevents deterministic validation.One nested JSON document
Rejected as the model-facing format because incomplete output cannot be safely consumed, deep delimiter balancing is fragile, and updates replace too much structure.Custom DSL as the wire format
Rejected because protocol consumers should not need the model parser, and durable records require stronger framing, versioning, and replay semantics.Positional component arguments
Rejected because inserting or reordering a catalog property can silently change meaning while remaining syntactically valid.Sandboxing every native component tree
Rejected because trusted renderer code never executes payload code. Universal iframes would degrade accessibility, focus, theme integration, and performance without strengthening this boundary. Sandboxing remains mandatory for executable extensions.Consequences
- The project maintains a parser and compiler in addition to protocol schemas.
- Language and protocol versions evolve independently from component catalogs.
- Model output can be optimized independently from the internal protocol.
- Protocol fixtures and the private Studio can replay without invoking a model.
- Additional renderers can consume the canonical graph without parsing the DSL.
- Validation errors can identify the exact statement, record, path, and policy that failed.
Follow-up decisions
- Define catalog negotiation and compatibility rules.
- Define stable public package and theme contracts.
- Define optional media/widget asset policies.
- Define optional forms/draft behavior without moving persistence into core.
- Production hosts must define action authorization and confirmation behavior.
- Benchmark the DSL against target model tokenizers and constrained decoders.