Headless Embodiment

Part of One User Interface — the app as its abstract self, embodiment on demand.

Status: one part ships, the rest is design.

Shipping (1.8.0): the level-0 demo below, and the property that makes the whole idea plausible — schematicSVG() is a pure function over plain records, so a map that arrived over a wire can be drawn by a process that never rendered the app. tosijs/state (DOM-free) means the state layer itself already runs headless, in plain node, with no shim.

Design, not code: the inversion itself — a headless app vending UI on demand, with humans and agents as co-equal session peers. Nothing on this page beyond the demo implements that, and no API for it exists. Read it as a direction, not a feature list.

The app is the model; the browser is just one embodiment. Below, level 0, live: a UI vended as a string, injected, and wired to live state with one bindParts call — type in the hydrated input:

import { elements, tosi, bindParts } from 'tosijs'

// elementsSSR: the elements proxy's call signature, emitting HTML strings
const VOID = new Set(['input', 'br', 'img', 'hr'])
const esc = (s) =>
  String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;')
const kebab = (s) => s.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase())
const elementsSSR = new Proxy(
  {},
  {
    get:
      (_, tag) =>
      (...args) => {
        const attrs = []
        const children = []
        for (const arg of args) {
          if (typeof arg === 'string' || typeof arg === 'number') {
            children.push(esc(arg))
          } else if (arg && arg.__html) {
            children.push(arg.__html)
          } else if (arg && typeof arg === 'object') {
            for (const [k, v] of Object.entries(arg)) {
              attrs.push(` ${kebab(k)}="${esc(v)}"`)
            }
          }
        }
        const t = kebab(tag)
        const html = VOID.has(t)
          ? `<${t}${attrs.join('')}>`
          : `<${t}${attrs.join('')}>${children.join('')}</${t}>`
        return { __html: html, toString: () => html }
      },
  }
)

// 1. VEND — a UI as a string; data-part markers stand where bindings will go
const ssr = elementsSSR
const html = String(
  ssr.div(
    ssr.input({ dataPart: 'name', placeholder: 'type your name' }),
    ssr.div('hello, ', ssr.span({ dataPart: 'greet' }))
  )
)

// show the vended string itself — wrapped, so narrow views stay readable
preview.append(
  elements.pre(html, {
    style: {
      whiteSpace: 'pre-wrap',
      wordBreak: 'break-all',
      maxHeight: '8em',
      overflow: 'auto',
      margin: 0,
    },
  })
)

// 2. HYDRATE — inject the markup, then one bindParts call wires it to state
const { vended } = tosi({ vended: { name: 'world' } })
const host = elements.div()
host.innerHTML = html
preview.append(host)
bindParts(host, {
  name: { bindValue: vended.name },
  greet: { bindText: vended.name },
})
import { updates } from 'tosijs'

// one sequential story, one test: inline tests within a fence run
// CONCURRENTLY (async test bodies are gathered with Promise.all), so a
// multi-step flow — hydrate, verify, interact, verify — must not be split
// across test() blocks that share state
test('the vended UI hydrates against live state, and the bindings are two-way', async () => {
  await updates()
  const input = preview.querySelector('[data-part="name"]')
  const greet = preview.querySelector('[data-part="greet"]')
  expect(greet.textContent).toBe('world')
  input.value = 'agent'
  input.dispatchEvent(new Event('change', { bubbles: true }))
  await updates()
  expect(greet.textContent).toBe('agent')
})

The inversion: headless embodiment ("server-side rendering of an MCP")

Follow the thesis to its end and the priority flips. If the agent interface is paths + wiring + actions, then the DOM was never the application — it was one projection of it. So run the projectionless app:

This is less speculative than it sounds, because every load-bearing piece has a precedent in the stack today:

Mechanics: vending is closer than it looks

Three levels of headlessness, all grounded in existing machinery:

Honest cost note (as human-facing SSR). Vending sidesteps the virtual-DOM diffing engine, but not hydration itself: the client still ships markup, then runs JavaScript (bindParts, component self-hydration) before the page is interactive. That cost is real and inherent to any client-side runtime — what's different here is its size and shape: no framework re-render to reconcile against, no serialized component tree to replay — just re-attaching bindings to markers, roughly proportional to the number of bound elements rather than to the size of the app. Lighter, not free.

"Virtual DOM," meaning it this time. Level 1 deserves its own framing: give the app a fake document.body and the "UI" simply lives there, unrendered. React's virtual DOM is a throwaway diffing artifact — cheap copies of a tree whose only purpose is to be discarded. This one is the opposite: it is the application's real structure, merely not painted. And that matters because the visual architecture a designer builds for human comprehension — containment, grouping, ordering, labels — is an information architecture, and it serves the AI consumer and the test engine exactly as it serves the eye. The DOM tree was the app's ontology all along; pixels were just one way to read it.

And note what falls out before any AI enters the picture: the headless embodiment is an intermediate, fully testable version of the application. Drive the whole app — state, actions, observers, declared wiring — with no browser, no DOM shim, no flake: write('app.filter', 'milk'), call('app.addItem', …), assert on read() and the observation stream. Today that seam is exactly where testing is weakest: unit DOMs (happy-dom) lie about interaction timing and Chromium-only lanes lie about other engines — the segmented saga (tosijs#21) shipped green through both. A headless embodiment splits the problem correctly: business logic and interaction semantics get fast, deterministic, DOM-free tests against the real application; real-browser lanes shrink to testing what they alone can test — the projection. The agent interface and the test harness turn out to be the same interface, which is the thesis again: one interface, and a test runner is just one more user.

The endgame framing: an app is a model plus a set of embodiments. Browser DOM for humans present now; prerendered HTML for humans arriving; MCP for agents; sync peers for other machines; the test harness, which is an agent that never gets bored — and the schematic (see The Derived Surface): the wiring drawn as labeled rectangles, a map view for humans and a token-compressed overview for vision models. tosijs is the model and the switchboard.