Review: App model & packaging
This review takes the app model as described and asks
three questions: what verifiably holds, what is built but idle, and what still
needs design. The lens is the sovereignty litmus (inspect what it sees · fork
permissions · BYO models + keys · movable memory · auditable actions · exit
without loss). Verdicts cite code at xenon-launcher main @56e94575 and
xe-example-apps @5752a3e; gap IDs (AM-*) are this beat’s app-model slice and
feed Part IV. The backend/engine half of the beat
is reviewed separately.
What verifiably holds
- One substance, one axis — and the code agrees. The XAM manifest carries
identity, source, and components as first-class fields, and the driver
(
Cf | Iwa | Origin | Oci | Compose) — not the packaging — decides how a component renders (model.rs:299). The spectrum of d/076 is expressible, not aspirational. - The two consent gates are unforgeable by construction. A
BuildGrantcan only be minted from a gate-1 consent whose digest equalssha256(plan)(adapter.rs:93), and every adapter re-checks it atmaterialize. The runtime refuses to build a runnable project without gate-2 consent matching the locked release digest, and private fields make the ungated path unreachable (runtime.rs:138). You cannot forget to check. - Unsupported features fail the install closed, early. A non-empty
unsupported_featureslist prevents materialization before anything is fetched or built (adapter.rs:45,compose.rs:1498). The default is “no,” not “try and see.” static-v0is proven end to end. excalidraw installs viastatic-v0/Origin, renders live, and is byte-identical across both peers (P-211). This is the exemplar of the d/101 default tier.- Compose service apps are proven live. guestbook and compose-board run through the full install → start → serve → recover loop (P-141 lifecycle + P-203 auto-wired end-to-end). See the engines review.
- Exit is a mandatory decision, not an afterthought.
UninstallTransactionforces a data disposition before delete —VerifiedExportThenDelete | RetainDetached | DestructiveDeleteConfirmed(lifecycle.rs:299) — andexport_xaaXAA archives already back update snapshots. Named volumes survive a stop.
Built, but idle
- Half the source kinds have no adapter.
Image,Web, andOciare expressible in the manifest (model.rs:230) but have no adapter behind them. A manifest can declare them; nothing installs them. - Reserved manifest families.
handlers,interfaces, andnotificationsare parsed and stored but not acted on — headroom, honestly labelled. - The swbn / IWA super tier. Fully built and tested (
swbn.rs), but under d/101 it is reserved for shells (console/hello-xe). The two dev-signed swbn example apps (scratchpad,appdex) are residue of the earlier per-app-IWA direction, not the default. Nothing forces swbn on an ordinary app — but the residue is a live cleanup item (AM-2 below).
Gap register (this beat, app-model slice)
IDs are beat-local; Part IV consolidates.
AM-1 — The rendering model still contradicts itself (d/103 item A)
Exists: d/103 item A is DECIDED — a compose app’s browser-facing service renders as a whole-origin proxy route on the plane, framed by Prism like every other app; no per-app Controlled Frame special-casing.
The gap: the tree has not caught up. RecipeOperation::MarkUi’s comment
still says “Controlled Frame UI” (compose.rs:49), and compose_components
maps x-xenon-component:ui → RuntimeDriver::Cf (compose.rs:1225). This is
the residual CF-per-app intent d/103 wants reconciled to plane rendering. Until
it is, a reader of the code sees two contradictory rendering stories.
Options: (a) reconcile MarkUi semantics to “browser-facing UI served via
the plane” and surface its Http endpoint into the runtime manifest — the P-210
work; (b) leave it until P-210 dispatches. Decides: reversible → orchestrator
(the decision is made; this is code hygiene). Gated behind AM-4 and the
engines review’s P-174 sequencing.
AM-2 — swbn residue vs the d/101 default
Exists: d/101 makes plain HTTP the default; swbn/IWA is the rare super tier.
The gap: SourceKind::Swbn, RuntimeDriver::Iwa, and the full swbn.rs
adapter are all present and tested, and two example apps still ship dev-signed
swbn. None of this is wrong — it is the legitimate super-tier machinery — but
the example set does not yet model the default cleanly.
Options: (a) migrate scratchpad/appdex to the plain-HTTP/origin tier so
the examples teach the default; (b) relabel them explicitly as super-tier
demonstrations. Decides: reversible → orchestrator.
AM-3 — App identity / signing, end to end
Exists: the catalog is signed by an ORCH-BUILD Ed25519 key
(package-index/src/lib.rs:32, trust label ORCH-SIGNED), successor-signing
verifies the committed catalog before signing serial N+1
(sign_catalog_successor, :187), and a retired key + catalog_key_migration
record show a rotation was already exercised. Per d/066 the orchestrator holds
keys for the build phase; every trust label shows the interim ORCH-SIGNED / DEV
tier.
The gap: compose apps are always UnsignedDerived (compose.rs:383) —
identity is URL + subdir, with no publisher key. Trust-on-first-install versus
update relies on a package_id match plus carried consent, with no
per-publisher key pinning across updates. The real signing ceremony is deferred
(P-075 rotates all of d/066 out).
Decides: irreversible / security → Luke + Jan (production ceremony). This is not an orchestrator call.
AM-4 — Route type for compose UIs (d/103 item B) — PROPOSED
Exists: P-203 shipped the whole-origin compose case as a backend-proxy
route with an empty digest + prefix / — an overload that does not touch
the P-148-frozen route-table type discriminator. origin::generate already
emits that shape (origin.rs:534).
The gap / open decision: d/103 item B is PROPOSED, not ratified. Accept
P-203’s backend-proxy overload as the documented whole-origin shape, or ratify
a distinct additive container-origin route type? P-210 (the Immich-class
whole-origin render) is blocked on this ruling.
Options: (a) accept the overload as the documented shape (schema-conservative,
already tested); (b) add container-origin as an additive type (cleaner
invariant: backend-proxy keeps meaning “static digest + proxied prefix”).
Decides: Luke (frozen-schema addition). Reversal is free — nothing more
is built until the gates clear.
AM-5 — First-class export UX (exit without loss)
Exists: the uninstall transaction makes data disposition mandatory, and XAA export archives exist for update snapshots.
The gap: there is no first-class per-app export command surfaced, and the
uninstall browser-storage sweep “does not independently prove localStorage
emptiness” (documented limit, serving-and-placement-design.md:93).
Options: (a) add an xe app export command backed by the existing XAA
machinery; (b) add a DOMStorage-receipt follow-up so uninstall can prove the
sweep. Decides: reversible → orchestrator.
Sovereignty litmus check
| Litmus | App-model verdict |
|---|---|
| Inspect what it sees | Strong. XAM is human-readable; Plan Review shows declared build effects (network hosts, context roots, resources, emulation); capabilities carry required reason strings. |
| Fork permissions | Holds. Capabilities are declared, gated, journaled, and revocable (L2, d/076); the two consent gates bind to exact digests. |
| BYO models + keys | Partial. Publisher signing is ORCH-held today (d/066); compose apps are unsigned-derived with no BYO-publisher-key story yet (AM-3). |
| Movable memory | Partial. XAA export archives + mandatory uninstall disposition exist; no first-class per-app export UX (AM-5). |
| Auditable actions | Holds. Install transitions write a signed audit; consent requests move pending → claimed → decided as inspectable records. |
| Exit without loss | Partial. Uninstall forces VerifiedExportThenDelete / RetainDetached / DestructiveDeleteConfirmed; the localStorage sweep cannot yet prove emptiness (AM-5). |
The model scores well on inspect / fork / audit — its consent design is its strongest sovereignty asset. The two soft spots are both about keys (AM-3) and getting your data out cleanly (AM-5).
Questions for the Luke + Jan session
- d/103 item B ratification (AM-4). Accept P-203’s shipped
backend-proxyempty-digest overload as the documented whole-origin shape, or ratify a distinctcontainer-originroute type? P-210 is blocked on this. (Luke) - Signing ceremony timing (AM-3). d/066 has the orchestrator holding
ORCH-SIGNED keys with a demonstrated rotation. When does P-075 (Luke + Jan
offline root) run — and does compose’s always-
UnsignedDerivedidentity get a publisher-key story, or stay URL-derived? (Luke + Jan) - Example-set hygiene (AM-2). Should the dev-signed swbn examples migrate to the plain-HTTP default tier so the example set teaches d/101 cleanly, or be explicitly relabelled super-tier? (Fable to dispatch; confirm intent)