Gap analysis for the first end-to-end product artifact: TodoMVC, multi-device, multi-user, realtime + non-realtime sync, E2E-encrypted, revocation live — the UI surface spike (#15/#16, spikes/todomvc/) joined with the engine spikes (#8/#9, spikes/skeleton/) and the storage layer (#19, spikes/storage/).
Target demo
Alice on two devices + Bob as a collaborator. Alice creates a todo list; edits flow device↔device and Alice↔Bob in realtime (component-iroh) and through a bucket when peers are offline (the pull layer); Bob is revoked mid-demo — his UI stops receiving, his pulls are refused, his history stays readable; a cold device bootstraps from the bucket alone.
What already exists (all green, pinned, unpatched upstreams)
| Piece |
Where |
Status |
| App-drives-DOM (no app JS), op protocol, deltic browser host |
spikes/todomvc (#16) |
running, live Pages demo |
| E2E content: automerge chunks under BeeKEM epochs, sedimentree sync over iroh QUIC, membership over the wire, keyhive-gated pull |
spikes/skeleton (3a+3b) |
running (wasmtime) |
| Bucket path: SigV4 in-guest, name-keys, K_p, cooperative revocation, account-less recipient, stolen-device assertion |
spikes/storage |
running (wasmtime + MinIO) |
| Identity: one platform-held key backing keyhive + subduction |
spikes 1–3 |
running |
| Relay interop with stock/n0-prod iroh relays |
polymorph-iroh |
proven |
Nothing below requires new design decisions on transports, crypto, or revocation semantics — those are proven. The gaps are integration, one content-model generalization, one identity-persistence decision, and one browser-host module.
Gaps
G1 — The app↔engine data contract (blocking, small, needs UI-session coordination).
The TodoMVC guest holds its model in memory. The demo needs the app world to grow a data import (or the app composed against the engine's export): todo-shaped for the demo (list/add/toggle/edit/delete + a change-notification stream) rather than the general polymorph:automerge surface (#8) — but shaped so it obviously projects from it. Decide together with the UI session: import-into-app-world vs wac plug app+engine; how change events reach on-event (the surface's event-record path suggests: engine change → framework event record).
G2 — Engine content model: chain → DAG (medium).
The skeleton's spine is a single "msg" key with linear preds. TodoMVC needs real automerge changes as chunks (multi-parent deps = keyhive pred-refs = sedimentree parents — the mapping exists, the plumbing assumes one pred), merge-on-read of concurrent edits, and offline authoring that reconciles later. causal_order already handles DAGs; the authoring/read paths need the generalization, plus doc persistence beyond the "msg" projection (todo list schema).
G3 — User = device group (medium; upstream-supported).
Spikes used per-device doc membership with host-relayed key blobs (labeled stand-ins). The demo story needs keyhive groups: enrollment (paste/QR a contact card; first device adds the new device to the user group), docs delegated to the group, sharing = adding Bob's group. This is #10's minimal slice; everything needed exists in keyhive_core (generate_group, transitive delegation — spike-proven at the doc level).
G4 — One engine, both sync paths (medium-large).
Skeleton (iroh) and storage (bucket) have never run together. Unify: the same keyhive envelope bytes feed sedimentree (realtime) and bucket objects (non-realtime; names already keyed by cref). Replace the storage spike's stand-ins with the real coupling: name-key epochs rotate with BeeKEM epochs; the keyhive op stream stored as blobs under name-keys (this is what makes cold-start and offline-invite work from the bucket alone); K_p wrapped to keyhive contact-card prekeys instead of ad-hoc x25519. Policy: author → put chunk+manifest (+op-stream delta); boot → pull bucket, then live-sync.
G5 — Persistence across restarts (decision + small-medium; gates the browser demo's UX).
Engine state (keyhive archive, sedimentree store, automerge docs) must survive reload: files under wasmtime; IndexedDB/OPFS under deltic. The identity half is the #11 finding: non-extractable keys don't persist without the webcrypto platform-keystore slice (deltic host could structured-clone CryptoKeys into IndexedDB — a host-module feature, not WIT). Decide: (a) memory-only demo identities + re-enroll per session, (b) demo-grade extractable identity persisted encrypted, (c) build the deltic keystore slice now. (c) is the honest one and pays #11 down early.
G6 — The engine's browser leg (medium-large + one empirical risk).
The UI already runs on deltic; the engine has only run under wasmtime. Needed: a deltic wasi:http@0.3 client host module (fetch-backed — likely does not exist yet; the storage fetcher component stays unchanged); verify inter-task-wakeup under deltic (the spike-2 finding was validated on wasmtime only — this is the one thing that could force rework); bucket CORS recipe (MinIO/R2 config — the provider-checker item); compose and size the full browser engine (keyhive + subduction + bridge + automerge + storage + fetcher + iroh endpoint).
G7 — Demo choreography and infra (small).
Relay (local --dev or n0 prod — proven), MinIO or R2 with CORS, a scripted scenario driver (offline window, revocation finale), Pages packaging alongside the existing demo. Stretch: presence via subduction_ephemeral.
Suggested sequencing
- G1 now — it unblocks parallel work between this session and the UI session; everything else hangs off the contract.
- G2+G3 → "engine v0.2", validated headless under wasmtime with the full multi-device/multi-user scenario (no UI needed — the spike-host pattern).
- G4 folds the bucket in (same headless validation: cold-start from bucket only).
- G5 decision early (it shapes G6's UX); implementation alongside G6.
- G6 browser leg — with the deltic
inter-task-wakeup check first, since it's the only rework-risk item.
- G7 assembly.
Open questions routed to the UI session: contract shape for G1 (import vs composition; change-event delivery through the event-record path); whether the worker/frame-split seam lands before or after the demo (the engine doesn't care, the threat story does).
Gap analysis for the first end-to-end product artifact: TodoMVC, multi-device, multi-user, realtime + non-realtime sync, E2E-encrypted, revocation live — the UI surface spike (#15/#16,
spikes/todomvc/) joined with the engine spikes (#8/#9,spikes/skeleton/) and the storage layer (#19,spikes/storage/).Target demo
Alice on two devices + Bob as a collaborator. Alice creates a todo list; edits flow device↔device and Alice↔Bob in realtime (component-iroh) and through a bucket when peers are offline (the pull layer); Bob is revoked mid-demo — his UI stops receiving, his pulls are refused, his history stays readable; a cold device bootstraps from the bucket alone.
What already exists (all green, pinned, unpatched upstreams)
spikes/todomvc(#16)spikes/skeleton(3a+3b)spikes/storageNothing below requires new design decisions on transports, crypto, or revocation semantics — those are proven. The gaps are integration, one content-model generalization, one identity-persistence decision, and one browser-host module.
Gaps
G1 — The app↔engine data contract (blocking, small, needs UI-session coordination).
The TodoMVC guest holds its model in memory. The demo needs the app world to grow a data import (or the app composed against the engine's export): todo-shaped for the demo (
list/add/toggle/edit/delete+ a change-notification stream) rather than the generalpolymorph:automergesurface (#8) — but shaped so it obviously projects from it. Decide together with the UI session: import-into-app-world vswac plugapp+engine; how change events reachon-event(the surface's event-record path suggests: engine change → framework event record).G2 — Engine content model: chain → DAG (medium).
The skeleton's spine is a single "msg" key with linear preds. TodoMVC needs real automerge changes as chunks (multi-parent deps = keyhive pred-refs = sedimentree parents — the mapping exists, the plumbing assumes one pred), merge-on-read of concurrent edits, and offline authoring that reconciles later.
causal_orderalready handles DAGs; the authoring/read paths need the generalization, plus doc persistence beyond the "msg" projection (todo list schema).G3 — User = device group (medium; upstream-supported).
Spikes used per-device doc membership with host-relayed key blobs (labeled stand-ins). The demo story needs keyhive groups: enrollment (paste/QR a contact card; first device adds the new device to the user group), docs delegated to the group, sharing = adding Bob's group. This is #10's minimal slice; everything needed exists in keyhive_core (
generate_group, transitive delegation — spike-proven at the doc level).G4 — One engine, both sync paths (medium-large).
Skeleton (iroh) and storage (bucket) have never run together. Unify: the same keyhive envelope bytes feed sedimentree (realtime) and bucket objects (non-realtime; names already keyed by cref). Replace the storage spike's stand-ins with the real coupling: name-key epochs rotate with BeeKEM epochs; the keyhive op stream stored as blobs under name-keys (this is what makes cold-start and offline-invite work from the bucket alone); K_p wrapped to keyhive contact-card prekeys instead of ad-hoc x25519. Policy: author → put chunk+manifest (+op-stream delta); boot → pull bucket, then live-sync.
G5 — Persistence across restarts (decision + small-medium; gates the browser demo's UX).
Engine state (keyhive archive, sedimentree store, automerge docs) must survive reload: files under wasmtime; IndexedDB/OPFS under deltic. The identity half is the #11 finding: non-extractable keys don't persist without the webcrypto platform-keystore slice (deltic host could structured-clone
CryptoKeys into IndexedDB — a host-module feature, not WIT). Decide: (a) memory-only demo identities + re-enroll per session, (b) demo-grade extractable identity persisted encrypted, (c) build the deltic keystore slice now. (c) is the honest one and pays #11 down early.G6 — The engine's browser leg (medium-large + one empirical risk).
The UI already runs on deltic; the engine has only run under wasmtime. Needed: a deltic
wasi:http@0.3client host module (fetch-backed — likely does not exist yet; the storage fetcher component stays unchanged); verifyinter-task-wakeupunder deltic (the spike-2 finding was validated on wasmtime only — this is the one thing that could force rework); bucket CORS recipe (MinIO/R2 config — the provider-checker item); compose and size the full browser engine (keyhive + subduction + bridge + automerge + storage + fetcher + iroh endpoint).G7 — Demo choreography and infra (small).
Relay (local
--devor n0 prod — proven), MinIO or R2 with CORS, a scripted scenario driver (offline window, revocation finale), Pages packaging alongside the existing demo. Stretch: presence viasubduction_ephemeral.Suggested sequencing
inter-task-wakeupcheck first, since it's the only rework-risk item.Open questions routed to the UI session: contract shape for G1 (import vs composition; change-event delivery through the event-record path); whether the worker/frame-split seam lands before or after the demo (the engine doesn't care, the threat story does).