Skip to content
sign in

==> read the decisions

ADR-0004: Project UIs are custom elements loaded into one app

Status: Accepted. Amended by ADR-0017 (2026-10-14): the shadow root carries the shared Tailwind theme and components. Amended by ADR-0023 (2026-10-15): the UI runs in a sandboxed frame, not in the app's page.

The shell must be consistent across every project, and a project must be shippable without a platform deploy.

  • One Next app is the shell. / lists that user's projects, rendered by the platform from the registry.
  • /p/<slug> resolves the project for (session user, slug) and loads its module, which defines cs-project-<slug>. The app renders that element.
  • Single origin, no iframe.
  • Theming crosses the boundary by CSS custom properties; auth by the same-origin session.
  • React is not shared across the boundary at all, so there is no version skew and no runtime router contract.
  • Shipping a project is: build, publish an immutable artifact, update the registry row. No platform deploy, no platform code change.
  • /p/<slug> authorizes on OWNERSHIP, not on being logged in, and answers 404 rather than 403 so it does not confirm other users' slugs.
  • A project shares its own user's page, so it is NOT isolated from that user's shell. Per-user artifact paths are what keep one user's module out of another's page.
  • If a project ever has to be untrusted relative to its own user, an iframe becomes necessary; that is the only case that needs one.
  • Building the UIs into the app: simplest, but a project change becomes a platform deploy, which defeats the independence.
  • A per-user origin: the wildcard is cheap, but it does not separate the module from the session, so it buys nothing that per-user artifact paths do not already give.