You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/adr/0081-trusted-react-page-tier.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,7 @@
16
16
1.**[rename]`kind:'jsx'` → `kind:'html'`.** The constrained, parse-never-execute tier (ADR-0080) is renamed to match what it is: author-written **HTML + Tailwind** (expressed as constrained JSX) compiled to the SDUI tree. `'jsx'` stays as a **deprecated alias** (already-saved pages keep loading); all authored examples/docs move to `'html'`.
17
17
2.**[capability] The `html` tier now resolves the full safe native HTML tag set.** Previously only `div/span/table/code/label` + semantic sectioning tags were registered, so `<h1>/<p>/<a>/<ul>/<li>/<img>/<blockquote>…` failed as `unknown-component`. They are now registered as passthrough renderers — the tier is finally honest about "HTML".
18
18
3.**[new tier]`kind:'react'` executes real React.** Its `source` is real JS/JSX (hooks, `.map`, event handlers, expressions), transpiled (Sucrase) and evaluated **in the main React tree — no sandbox** — by the new `@object-ui/react-runtime`. This is `parse = execute`: the categorical opposite of the `html` tier.
19
-
4.**[security = trust, not sandbox]`react` is gated by a host capability that defaults ON.**`CAP_REACT_PAGES` is controlled only by the *host*, never by authored metadata. It defaults ON because ObjectStack pages are authored by trusted authors and pass human review (draft-gating, ADR-0033). A deployment that does NOT trust its authors disables it **server-side** — the ObjectStack runtime injects the disable global when `OS_DISABLE_REACT_PAGES` is set (one env var, no rebuild). A sandbox (iframe/worker) is the tool for *untrusted* execution; we deliberately choose **trust + review** instead, which keeps the author's code a first-class citizen of the app (shared React tree, real components, real data). This is NOT an enterprise-only feature.
19
+
4.**[security = trust, not sandbox]`react` is gated by a host capability that defaults ON.**`CAP_REACT_PAGES` is controlled only by the *host*, never by authored metadata. It defaults ON because ObjectStack pages are authored by trusted authors and pass human review (draft-gating, ADR-0033). A deployment that does NOT trust its authors disables it **server-side** — the ObjectStack runtime injects the disable global when `OS_PAGE_REACT=off`(one env toggle, no rebuild). A sandbox (iframe/worker) is the tool for *untrusted* execution; we deliberately choose **trust + review** instead, which keeps the author's code a first-class citizen of the app (shared React tree, real components, real data). This is NOT an enterprise-only feature.
20
20
5.**[runtime] Vendored, not depended.**`@object-ui/react-runtime` inlines the ~150-LOC react-runner core (MIT) so we fully own the injected **scope/imports surface** (the actual capability ceiling) and can **lazy-load** it — the transpiler ships in a separate chunk fetched only when a `react` page renders *with the capability on*.
21
21
6.**[scope] Inject data blocks + React; leave layout to HTML.** The runtime scope exposes `React`, the curated **public data blocks** (`<ObjectTable>`, `<ObjectForm>`, charts, metrics — each a prop-driven wrapper over `SchemaRenderer`), a `<Block type=…/>` escape hatch, and the page `data`. Layout/structure is plain HTML + Tailwind (React's strength) — we do **not** bridge React children into schema-children renderers.
22
22
7.**[lint]`react` is not linted by the constrained parser.**`validate-jsx-pages` (ADR-0080's build gate) validates `html`/`jsx` only; running the constrained JSX parser over real React source would false-error on hooks and expressions. The `react` tier's safety boundary is the **capability + human review**, not a static gate.
@@ -31,7 +31,7 @@
31
31
| Processing |**parsed**, never executed → SDUI tree | transpiled + **executed** in the main React tree |
32
32
| JavaScript | none (static literals) | full |
33
33
| Safety model | safe by construction (no execution) |**trust** (capability + review); no sandbox |
34
-
| Where it runs | OSS default ON | default ON; disable server-side via `OS_DISABLE_REACT_PAGES`|
34
+
| Where it runs | OSS default ON | default ON; disable server-side via `OS_PAGE_REACT=off`|
@@ -49,7 +49,7 @@ Register the safe native HTML flow/inline set (`h1–h6, p, a, ul/ol/li, dl/dt/d
49
49
50
50
### 3. Add `react` behind a host capability that defaults ON
51
51
-**Gate**: `@object-ui/core` gains `enableCapability`/`disableCapability`/`isCapabilityEnabled` + `CAP_REACT_PAGES`. `react-pages` defaults ON. Host-only — it reads `globalThis.__OBJECTUI_CAPABILITIES_DISABLED__` (force off) and `__OBJECTUI_CAPABILITIES__` (force on); it is structurally impossible to flip from authored metadata.
52
-
-**Server opt-out**: the ObjectStack console serving (framework `cli/utils/console.ts`) injects the disable global into the served HTML when `OS_DISABLE_REACT_PAGES` is set — one env var, read per request, no rebuild. This is the single server-side switch a deployment uses to turn the tier off.
52
+
-**Server opt-out**: the ObjectStack console serving (framework `cli/utils/console.ts`) injects the disable global into the served HTML when `OS_PAGE_REACT=off`— one env toggle, read per request, no rebuild. This is the single server-side switch a deployment uses to turn the tier off.
53
53
-**Renderer**: PageRenderer routes `kind:'react'` to a renderer that — when the capability is on — lazy-imports `@object-ui/react-runtime`, builds the scope, and renders via its error-boundaried `ReactRunner`. Capability off → a clear "React pages are disabled on this deployment" notice, not a crash.
54
54
55
55
### 4. Vendor the runtime; own the scope
@@ -63,4 +63,4 @@ Register the safe native HTML flow/inline set (`h1–h6, p, a, ul/ol/li, dl/dt/d
63
63
-**Two safety stories, each clean.**`html` is safe by construction and stays the OSS/multi-tenant default. `react` is safe by *trust*, opt-in, and obviously so (you typed `enableCapability`).
64
64
-**No build/deploy for either.**`html` compiles at save; `react` transpiles at render (lazy chunk). Neither needs a bundler in the author loop.
65
65
-**AI fit.** AI writes both fluently. For `react`, correctness is bounded by the injected scope + human review (ADR-0033), not by hoping the model never writes a bad effect.
66
-
-**Cost.**`react` runs author code in the main tree: a thrown render is caught by the error boundary, but an author can still write a slow/incorrect page. That is the accepted price of the trusted tier; a deployment that does not trust its authors flips `OS_DISABLE_REACT_PAGES`. Revisit if a future need arises for *untrusted* rich pages — that would be a *different* ADR (sandboxed execution), not this one.
66
+
-**Cost.**`react` runs author code in the main tree: a thrown render is caught by the error boundary, but an author can still write a slow/incorrect page. That is the accepted price of the trusted tier; a deployment that does not trust its authors sets `OS_PAGE_REACT=off`. Revisit if a future need arises for *untrusted* rich pages — that would be a *different* ADR (sandboxed execution), not this one.
0 commit comments