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
- The experience service serves UI documents at L2: a tree of registered coded components, each named
key@major, with Zod-validated props. - Data binds to
@Queryaction keys; the client calls them batched throughexperience.query.batch. - Events bind to
@Actionkeys or to a closed set of UI verbs (navigate, open, close, refresh and the like). - No code, markup, CSS or head content travels in a document, ever.
- 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.
- 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.