rspace-online/backlog/tasks/task-46 - Implement-Cross-A...

8.6 KiB

id title status assignee created_date updated_date labels milestone dependencies references priority
TASK-46 Implement Cross-App Embedding: r-ecosystem apps in rSpace canvases Done
2026-02-18 20:07 2026-03-12 01:08
feature
phase-5
ecosystem
m-1
TASK-41
TASK-42
rspace-online/lib/shape-registry.ts
rspace-online/server/index.ts
rspace-online/website/canvas.html
high

Description

Allow r-ecosystem apps (rWallet, rVote, rMaps, etc.) to embed their live UI into rSpace canvases via dynamically loaded Web Components.

Ecosystem App Manifest Protocol:

  • Each app hosts /.well-known/rspace-manifest.json
  • Manifest declares: appId, name, icon, moduleUrl, shapes[] (tagName, defaults, portDescriptors, eventDescriptors)

New file lib/ecosystem-bridge.ts:

  • EcosystemBridge class: loadManifest(), loadModule(), createSandboxedEmbed()
  • Two embedding modes:
    1. Trusted (Web Component): dynamic import(), shares CRDT directly, full port/event access
    2. Sandboxed (iframe): postMessage bridge for untrusted apps, limited API

New Automerge shape type:

  • type: "ecosystem-embed", appId, moduleUrl, config, sandboxed boolean

Server additions:

  • GET /api/ecosystem/:appId/manifest — proxy to avoid CORS
  • Server pre-fetches and caches ecosystem manifests

Canvas.html additions:

  • Dynamic toolbar section for ecosystem apps (loaded from manifests)
  • Lazy module loading on first use
  • Service Worker caches modules for offline

Runtime:

  1. Server fetches ecosystem manifests → toolbar shows app buttons
  2. User adds ecosystem shape → module import()-ed → Web Component registered → shape created
  3. Remote sync: create-shape for ecosystem-embed triggers lazy module load on other clients
  4. Embedded components participate in port/event system like native shapes

Acceptance Criteria

  • #1 Ecosystem manifest protocol defined and documented
  • #2 EcosystemBridge loads manifests and dynamic imports modules
  • #3 Trusted Web Components share CRDT and port/event system
  • #4 Sandboxed iframe mode works with postMessage bridge
  • #5 Server proxy avoids CORS for manifest/module loading
  • #6 Toolbar dynamically shows ecosystem app buttons
  • #7 Remote clients lazy-load modules when ecosystem shapes appear
  • #8 Service Worker caches ecosystem modules for offline

Implementation Notes

POC implemented in commit 50f0e11: folk-rapp shape type embeds live rApp modules as iframes on the canvas. Toolbar rApps section creates folk-rapp shapes with r-prefixed moduleIds. Module picker dropdown, colored header with badge, open-in-tab action, Automerge sync. Remaining: manifest protocol, EcosystemBridge, sandboxed mode, Service Worker caching, remote lazy-loading.

Enhanced in 768ea19: postMessage bridge (parent↔iframe context + shape events), module switcher dropdown, open-in-tab navigation. AC#7 (remote lazy-load) works — newShapeElement switch handles folk-rapp from sync.

Status check 2026-03-11

folk-rapp shape, postMessage bridge, module switcher, toolbar rApps section all committed. AC #6 and #7 working. Remaining: manifest protocol spec (AC #1), EcosystemBridge class (AC #2), trusted CRDT sharing (AC #3), sandboxed iframe postMessage (AC #4), server manifest proxy (AC #5), SW caching (AC #8). Depends on TASK-41 (shape registry) and TASK-42 (data pipes).

Assessment 2026-03-11 (detailed code review)

AC #1 — Ecosystem manifest protocol defined and documented: NOT DONE

No /.well-known/rspace-manifest.json file, schema, or protocol documentation exists anywhere in the codebase. The only reference is in this task's description. The MODULE_META hardcoded record in lib/folk-rapp.ts:19-44 serves as a static stand-in, but it is not a discoverable manifest protocol — it is baked into the folk-rapp component.

AC #2 — EcosystemBridge loads manifests and dynamic imports modules: NOT DONE

No lib/ecosystem-bridge.ts file exists. No EcosystemBridge class anywhere. The folk-rapp component (lib/folk-rapp.ts) uses same-origin iframe embedding (line 845-873) and a hardcoded MODULE_META lookup (line 19-44) instead of dynamic manifest loading + import(). No dynamic import logic for external ecosystem modules.

AC #3 — Trusted Web Components share CRDT and port/event system: PARTIALLY DONE

  • Port system: folk-rapp does NOT implement portDescriptors, getPort(), setPortValue(), or onEventReceived(). It does not participate in the typed data pipe system from lib/data-types.ts / lib/folk-arrow.ts.
  • Event bus: folk-rapp does not subscribe to CanvasEventBus channels.
  • CRDT sharing: The community-sync.ts #postMessageToParent() (line 1281-1297) broadcasts shape-updated events to parent frames, and folk-rapp receives them via #handleMessage() (line 722-750). This is a one-way data bridge (iframe -> parent) via postMessage — NOT direct CRDT sharing.
  • Verdict: The postMessage bridge works for shape update forwarding, but there is no direct CRDT doc sharing, no port/event participation. NOT DONE per the AC's intent of "shares CRDT and port/event system".

AC #4 — Sandboxed iframe mode works with postMessage bridge: PARTIALLY DONE

  • folk-rapp has a working postMessage protocol (lines 12-16 doc comment):
    • Parent -> iframe: { source: 'rspace-parent', type: 'context', shapeId, space, moduleId } (line 756)
    • iframe -> parent: { source: 'rspace-canvas', type: 'shape-updated' } (line 733)
    • iframe -> parent: { source: 'rspace-rapp', type: 'navigate', moduleId } (line 747)
  • However, this is same-origin iframe embedding of internal rApps only. There is no sandbox attribute, no origin validation (uses '*' for postMessage target), and no structured API bridge for untrusted third-party apps. The AC envisions a security-conscious sandboxed mode for untrusted ecosystem apps — that does not exist yet.
  • Verdict: Basic postMessage works for internal rApps. The sandboxed-for-untrusted-apps mode is NOT DONE.

AC #5 — Server proxy avoids CORS for manifest/module loading: NOT DONE

No /api/ecosystem/:appId/manifest route exists in server/index.ts. No proxy endpoint for fetching external ecosystem manifests. The server/landing-proxy.ts buildEcosystemMap() function (line 124) is for rewriting standalone domain links in landing pages, not for proxying manifests.

AC #8 — Service Worker caches ecosystem modules for offline: PARTIALLY DONE

  • website/sw.ts has a PrecacheManifest system (lines 18-23) that caches core and modules arrays from /precache-manifest.json.
  • The activate handler lazy-caches module bundles (lines 64-96).
  • vite.config.ts generates the precache manifest at build time (line 1172-1183).
  • However, this caches the built-in rSpace module bundles (Vite output), NOT external ecosystem module URLs loaded at runtime. There is no mechanism to dynamically add ecosystem module URLs to the SW cache.
  • Verdict: SW caching infrastructure exists for internal modules. Ecosystem-specific module caching is NOT DONE.

Implementation 2026-03-12

Files created:

  • shared/ecosystem-manifest.ts — TypeScript types for the ecosystem manifest protocol (EcosystemManifest, EcosystemShapeDescriptor, EventDescriptor, ResolvedManifest, ECOSYSTEM_PROTOCOL_VERSION)
  • lib/ecosystem-bridge.ts — EcosystemBridge class (singleton) with loadManifest(), loadModule(), registerShapes(), createSandboxedEmbed(), SW cache notification, origin-validated postMessage handling

Files modified:

  • server/index.ts — Added /.well-known/rspace-manifest.json (self-manifest), GET /api/ecosystem/:appId/manifest (proxy with cache), GET /api/ecosystem/:appId/module (JS proxy)
  • lib/folk-rapp.ts — Added portDescriptors (data-in, data-out, trigger-in, trigger-out), initPorts(), onEventReceived(), sandbox attribute on iframe, origin-validated postMessage (AC#3+#4), forward port-value-changed to iframe, handle ecosystem-embed messages
  • website/sw.ts — Added ECOSYSTEM_CACHE, message handler for cache-ecosystem-module and clear-ecosystem-cache, preserved ecosystem cache during version cleanup

Summary of remaining work:

AC Status Blocking?
#1 Manifest protocol Not done Yes — foundation for #2, #5
#2 EcosystemBridge Not done Yes — core feature
#3 CRDT + port/event sharing Not done Depends on #2
#4 Sandboxed iframe Partial (internal postMessage works) Needs security hardening
#5 Server proxy Not done Depends on #1
#8 SW caching Partial (infra exists, not ecosystem-aware) Depends on #2