Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 BuildGrant can only be minted from a gate-1 consent whose digest equals sha256(plan) (adapter.rs:93), and every adapter re-checks it at materialize. 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_features list prevents materialization before anything is fetched or built (adapter.rs:45, compose.rs:1498). The default is “no,” not “try and see.”
  • static-v0 is proven end to end. excalidraw installs via static-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. UninstallTransaction forces a data disposition before delete — VerifiedExportThenDelete | RetainDetached | DestructiveDeleteConfirmed (lifecycle.rs:299) — and export_xaa XAA archives already back update snapshots. Named volumes survive a stop.

Built, but idle

  • Half the source kinds have no adapter. Image, Web, and Oci are 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, and notifications are 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

LitmusApp-model verdict
Inspect what it seesStrong. XAM is human-readable; Plan Review shows declared build effects (network hosts, context roots, resources, emulation); capabilities carry required reason strings.
Fork permissionsHolds. Capabilities are declared, gated, journaled, and revocable (L2, d/076); the two consent gates bind to exact digests.
BYO models + keysPartial. Publisher signing is ORCH-held today (d/066); compose apps are unsigned-derived with no BYO-publisher-key story yet (AM-3).
Movable memoryPartial. XAA export archives + mandatory uninstall disposition exist; no first-class per-app export UX (AM-5).
Auditable actionsHolds. Install transitions write a signed audit; consent requests move pending → claimed → decided as inspectable records.
Exit without lossPartial. 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

  1. d/103 item B ratification (AM-4). Accept P-203’s shipped backend-proxy empty-digest overload as the documented whole-origin shape, or ratify a distinct container-origin route type? P-210 is blocked on this. (Luke)
  2. 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-UnsignedDerived identity get a publisher-key story, or stay URL-derived? (Luke + Jan)
  3. 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)