Review: Shell & rendering (Prism / Xenon)
This review takes the layer as described and
asks three questions: what verifiably holds, what is built or proven but not
yet the design, and what still needs design. The lens throughout is the
sovereignty litmus (inspect what it sees · fork permissions · BYO models +
keys · movable memory · auditable actions · exit without loss). Verdicts cite
code at agent54/prism main @98acaa3 and the Xenon packet record; gap IDs
(G1–G6) are this beat’s register and feed Part IV.
The one line to carry out of this review: the highest-leverage undesigned sovereignty surface in the whole layer is G6 — the in-frame permission model. Whether a page’s request for the camera, microphone, or location is answered by the user’s own agent (shielded by default, journaled) or by Chrome’s native prompt decides whether the phrase “your agent answers for you” is real or marketing. It is not built for installed apps, and it is Luke’s call.
What verifiably holds
- The trust boundary is one readable line. The baked CSP
(
packaging/bundle/headers.json) reduces the shell’s outbound trust to two origins —127.0.0.1:5454for the catalog and*.localhost:5454for app panes — and nothing else. A reviewer can hold the entire network surface of the shell in their head. That is the sovereignty litmus’s inspect what it sees criterion, met by construction. - Guests are unprivileged by default, and the shell says how. Apps load
into partitioned Controlled Frames (
persist:prism-fleet-apps); the only way back to the shell is aMessageChannelgated by port identity, a 128-bit nonce, and an exact schema, carrying exactly four tab verbs (src/channel/message.ts:15,:41-63). Every accept and every reject is journaled. The boundary is small enough to audit. - Honesty is enforced, not hoped. A static lint forbids control ever
riding over console output (
scripts/check-channel-boundary.mjs); offline apps are shown greyed with a last-seen time rather than hidden (iwa-controlledframe-app.ts:161-193); the dev-signed trust tier is printed in the app’s own name. The journal is hash-linked and separately verifiable (journal:verify). - It runs on a real device. Prism installed and rendered a guest inside a
genuine
HTMLControlledFrameElementon an Android emulator (P-195, merged PR#17) — the Controlled-Frame story is proven, not aspirational.
Built or proven, but not yet the design
- Two engines (P-192). The same signed bundle rendered on Blink and Servo. Real, and strategically large (see below) — but the Servo path is CF-shaped, not CF-strong, so it is inspiration for a standards story, not a shippable second engine yet.
- Composition. A working, receipted carry between two apps exists — on
<iframe>, not Controlled Frames (web/index.html:28-29). The mechanism is proven; its migration onto the Controlled-Frame + catalog path is not built (G3). - The launch seam. The catalog-descriptor launch path is already the code
(
iwa-controlledframe-app.ts:258), but blessing it as the canonical cross-platform seam is a proposal awaiting Luke (d/104). The code has run ahead of the ratified design; the review flags the gap rather than pretending it is closed.
Desired vs current vs gap — the map
| Area | Desired | Current | Gap |
|---|---|---|---|
| App tier (d/101) | Most apps plain http(s); IWA reserved for the shell | Exactly this — only Prism is bundled; every proven guest ran plain http | — (holds) |
| Launch seam (d/104) | One catalog descriptor is the launch path, all platforms | Code already sets frame src = launch_url; Android can’t repoint installed guest | Ratify d/104 (G-seam) |
| In-frame permissions | Shell brokers every permission, shield-by-default, journaled | Only geolocation, only in the dev driver, same-origin | G6 — undesigned |
| Composition (d/103) | Any two fleet apps compose via CF + plane origins | Two fixture apps compose via iframes | G3 |
| Frame↔shell contract | App can title / notify / report liveness safely | Four tab verbs, one-way; no title/notify | G2 |
| Production identity | Real key ceremony, independent review | Dev-signed; name reads “(DEV-SIGNED)” | G1 |
| Local trust | Authenticated wake / bind of the plane | CSP trusts whatever binds the ports | G4 |
| Fork burden | Minimal patches, upstream-aligned | 3-patch series, no rebase cadence set | G5 |
Gap register (this beat — shell & rendering)
G6 — The in-frame permission model (the headline gap)
Exists: only geolocation, and only in the development driver, applied
same-origin (src/spectrum/permission.ts, src/gateway/gateway.ts:128-143).
The installed-driver mechanism is deliberately not built — when it cannot
mint a lease or journal the answer, the gateway refuses rather than fake a
result (permission.ts:4-13, gateway.ts:130-135). Answers can already be
allow, deny, or shield (return a user-chosen value; the page never sees the
real device).
Why design is needed: three questions are open and every one of them is a sovereignty question. Who prompts — the shell, or Chrome’s native dialog inside the partition? Who remembers — is there per-origin persistence the user can inspect and revoke? And the whole non-geolocation capability set (camera, microphone, clipboard, USB) is undesigned for the installed Controlled-Frame driver.
Options and trade-offs — presented for Luke to weigh:
| Option A — shell brokers everything | Option B — delegate to Chrome | |
|---|---|---|
| Who prompts | The user’s agent, via the agency ladder | Chrome’s native per-origin prompt |
| Default answer | Shield by default (user-chosen value) | Whatever Chrome offers (allow / block) |
| Record | Journaled, inspectable, revocable | Chrome’s site settings |
| Litmus fit | Inspect what it sees + auditable both met | Cedes “your agent answers for you” |
| Cost | Real browser-driver engineering per capability | Nearly free — it already works |
Who decides: Luke — this is litmus-touching. Option A is the entire differentiator of the product (“your agent answers for you”); Option B is free but concedes the sovereignty claim at exactly the surface where users feel it most. A design packet should precede any implementation.
G1 — Production signing / the key ceremony
Exists: development signing by key-path (d/066, orchestrator-held build-phase key); the installed name literally reads “(DEV-SIGNED)”. Why design is needed: the ceremony (offline root, Luke + Jan) is unbuilt; nothing yet rotates the dev key out or establishes a world-facing, independently reviewed identity. Options: (a) a single offline-root ceremony at world-release (current plan) — every install shows DEV until then; (b) an intermediate cross-signed release tier before public. Decides: Luke + Jan (key custody is a hard rule; d/066 is a bounded, build-phase-only override). Queued behind the publishing gate.
G2 — The frame↔shell communication contract
Exists: an asymmetric, minimal surface. App→shell is the four-verb nonce
channel; shell→app is a capability-leased gateway reaching the pane, plus an
opt-in cooperative bridge an app may expose (web/dev-origins/site.html).
Why design is needed: there is no contract for an app to set a window
title, raise a notification, request focus, or report liveness. A guest
sets document.title, but the shell has no defined reaction. Options:
(a) extend the nonce-authed verb set with a small app-declared surface
(title / notify / badge); (b) keep apps fully unprivileged and derive what the
shell needs by observation. Trade-off: (a) is ergonomic but every verb is a
capability whoever holds the port then holds (the channel doc’s own caveat);
(b) keeps the boundary tiny but apps cannot cooperate. Decides: Fable
proposes the verb-set delta; Luke on anything touching user-visible identity
(notifications).
G3 — Multi-app composition
Exists: the Composition surface frames two fixture apps and carries a leased
“carry” record between them (read → consent card with a payload fingerprint →
leased write → undo) — on iframes (web/index.html:28-29).
Why design is needed: composition across arbitrary catalog apps in
Controlled Frames (vs two hard-coded embeds) is aspired, not built; d/103
(compose UIs are plane origins) is the intended generalization. Options:
(a) promote composition onto the CF + catalog path so any two fleet apps
compose; (b) keep composition a curated shell feature over fixtures.
Decides: Luke (product surface).
G4 — The local-trust boundary
Exists: the CSP pins trust to 127.0.0.1:5454 + *.localhost:5454.
Why design is needed: the shell trusts whatever binds those ports.
d/053’s own gate says “unauthenticated loopback TCP is disqualifying by our own
standard” — yet the IWA→Boot-Service wake channel’s authentication is explicitly
OPEN (d/053:44-49). Options: per-install token provisioned at install; IWA
Direct Sockets with an authenticated handshake; or OS socket activation
(launchd / systemd / Windows service triggers). Decides: Luke — security and
litmus-touching; a design packet is required before implementation.
G5 — Chromium-fork maintenance burden
Exists: agent54/android-iwa is deliberately a patch series, not a full
fork — three patches (0181 / 0182 / 0183) on a stock Chromium base plus gn
args and validation scripts. Controlled Frame and IWAs are real upstream
Chromium features (origin trial), so the divergence is small and
upstream-aligned. Why design is needed: no rebase cadence, patch-count
budget, or upstreaming path is recorded. Options: (a) ride the
desktop-android variant + origin-trial features and drive patches toward zero as
upstream lands (lowest burden, dependent on upstream timing); (b) maintain a
standing carry-patch set (control at the cost of rebase toil). Decides:
Jan + Luke (fork strategy). Note d/098 — no agent-opened upstream PRs, ever;
upstreaming is a human-published contribution.
P-192 — Two engines, and the standards story
The single result most worth understanding strategically is that the same
signed bundle rendered on two independent engines: Blink via a real IWA install,
and Servo via a Rust isolated-app:// scheme handler that verifies the
bundle’s Ed25519 signature offline before serving a byte (a tampered bundle is
refused, test-pinned). This converts “a Chromium-fork feature” into “a
web-platform shape with more than one implementation” — which is precisely the
argument a standards body wants to hear, and precisely the position that keeps
the Xe Computer from being a single-vendor dependency.
The honesty caveat is load-bearing: the Servo side is CF-shaped, not CF-strong. It reproduces the API surface and the origin-verification discipline, but not the isolation fundamentals — no OS sandbox, no cross-origin-isolation implementation, permissions-policy commented out (P-190/P-191, PARKED). So P-192 is a credible standards and independence story and a reminder that a second production engine is real work, not a flag flip. The verdict on Servo as a base was INSPIRATION / NOT-VIABLE-YET, revisited the week of Jul 20.
Sovereignty litmus check
| Criterion | Verdict for this layer |
|---|---|
| Inspect what it sees | Holds by construction. The whole outbound trust surface is two CSP origins; the shell can executeScript-observe guests, and every observation is leasable and journaled. |
| Fork permissions | Partial — and this is G6. Permission shielding exists in the dev driver (geolocation), but the installed-app broker is unbuilt; the choice between shell-brokered and Chrome-native prompts is undesigned and decides whether this criterion is truly met. |
| BYO models + keys | Holds for shape, pending ceremony (G1). Signing keys are local and never escrowed; the dev key is honestly labelled; the production ceremony is ahead. |
| Movable memory | Structurally present. Guest state lives in a named partition; app data single-homing is a site-wide finding (Part IV), not this layer’s to close. |
| Auditable actions | Holds. Hash-linked journal, separately verifiable; channel rejections recorded with cause; leased reads receipted. |
| Exit without loss | Holds as intent, written into the product. “Swap this shell and keep your apps, your data, and your journal” (spectrum.ts:163-165) — Prism is interchangeable by design; the guarantee firms up once composition and the launch seam are the ratified path (G3, d/104). |
Questions for the Luke + Jan session
- In-frame permission model (G6, the headline). Does the shell broker all app permissions via the agency ladder — shield-by-default, journaled — or delegate to Chrome’s native per-origin prompts inside the partition? This is the highest-leverage undesigned sovereignty surface in the layer.
- Ratify d/104? Bless the catalog descriptor
(
label → http://<app>.localhost:5454) as the single Controlled-Frame launch seam on every platform, or hold out for an explicit install-time guest-manifest launch contract? (Unblocks the Android frame-cell.) - Wake-channel authentication (d/053 OPEN, G4). Per-install token, IWA Direct Sockets handshake, or OS socket activation for IWA→Boot-Service? This is the load-bearing piece of the local-trust boundary.
- Fork strategy (G5). Commit to minimizing
android-iwatoward zero patches as upstream Controlled Frame / IWA lands (accepting an upstream-timing dependency), or maintain a standing carry-patch set? Rebase cadence and patch-count budget are unset. - Composition scope (G3). Promote composition onto the Controlled-Frame + catalog path (any two fleet apps compose), or keep it a curated shell feature over the two fixtures?
- Production signing timing (G1). Confirm the key ceremony triggers at world-release and that nothing ships publicly under DEV-SIGNED.