Skip to main content
Amended by 0154, decision 4: tenant code runs only in the sandbox.
Amended by 0139: actions declare their user experience.

Context

0011 rejects “a runtime schema engine, server-driven UI, JSONB-for-everything” in favour of coded resources. Dashboards, page builders and tenant-customised screens need layouts that change without a deploy. The owner’s experience-service architecture document already makes that service the server-driven UI service, using versioned declarative schemas: component type, props, bindings, actions and permissions, with no code over the wire. Levels of server-driven UI were weighed; level L2 is a tree of registered, coded components with typed props. Higher levels ship logic or markup and are what 0011 rightly rejects.

Decision

  1. The experience service serves UI documents at L2: a tree of registered coded components, each named key@major, with Zod-validated props.
  2. Data binds to @Query action keys; the client calls them batched through experience.query.batch.
  3. Events bind to @Action keys or to a closed set of UI verbs (navigate, open, close, refresh and the like).
  4. No code, markup, CSS or head content travels in a document, ever.
  5. Three parts of the owner’s document are replaced: hand-written YAML registered at startup becomes gen manifests published by CI (0109); NestJS and an API gateway become an Encore service through gen; the Cloudflare edge becomes an optional cache only.
  6. Every document is validated server-side against the registry before it is stored or served: unknown component, unknown query or action key, bad props or a missing permission is refused.

Consequences

  • Tenants and agents can change layouts without a deploy, and every change is still checked against coded components and actions.
  • The 0011 rejection of a runtime schema engine and of JSONB-for-everything stands; only data that references registered code is allowed.
  • The validator and the component registry are security-critical code.

Enforcement

Planned: the document validator in @stackra/experience with a test per refusal case; a lint rule that no renderer evaluates a string as code or HTML. Nothing checks this today.

Supersedes and sources

Supersedes: the “A runtime schema engine, server-driven UI, JSONB-for-everything” row of 0011. The row is rewritten to reject a runtime schema engine, JSONB-for-everything and tenant data that carries code, markup or logic, and to allow data that only references registered code: L2 UI documents (here), builder-UI agents (0100 decision 8), tenant automations (0122) and bounded custom fields (0116). Sources: owner decisions in chat, 2026-10-19 (Round 11 X1, X3). Informational: text/experience-service-architecture-decision.md.