Skip to content

TodoMVC end-to-end demo: integrating the UI surface with the engine, both sync paths, and revocation #20

Description

@lannbot

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

  1. G1 now — it unblocks parallel work between this session and the UI session; everything else hangs off the contract.
  2. G2+G3 → "engine v0.2", validated headless under wasmtime with the full multi-device/multi-user scenario (no UI needed — the spike-host pattern).
  3. G4 folds the bucket in (same headless validation: cold-start from bucket only).
  4. G5 decision early (it shapes G6's UX); implementation alongside G6.
  5. G6 browser leg — with the deltic inter-task-wakeup check first, since it's the only rework-risk item.
  6. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions