---
id: TASK-118
title: 'Epic: Make all rApps multiplayer with "Pull rApplet to rSpace"'
status: Done
assignee: []
created_date: '2026-03-16 00:05'
updated_date: '2026-03-16 00:51'
labels:
  - epic
  - multiplayer
  - architecture
milestone: Multiplayer Everything
dependencies: []
priority: high
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
Ensure every rApp module has:
1. **Multiplayer real-time sync** via existing Automerge/local-first stack — see other participants' changes live
2. **"Pull rApplet to rSpace" button** — a standard UI pattern letting space owners pull/enable an rApp module into their space from a global catalog

## Current State (27 modules)
- **12 already have local-first/Automerge**: rbooks, rcal, rcart, rfiles, rflows, rinbox, rnotes, rsocials, rsplat, rtasks, rtrips, rvote
- **2 use ephemeral WebSocket sync** (no Automerge): rmaps, rnetwork
- **13 have NO real-time sync**: rchoices, rdata, rdesign, rdocs, rforum, rmeets, rphotos, rpubs, rswag, rtube, rwallet, rspace, rschedule

## "Pull rApplet to rSpace" Pattern
A standardized UI component (`folk-applet-pull.ts`) that:
- Shows available rApps as cards in a global catalog
- Space owners can enable/disable modules per-space via PATCH `/:space/modules`
- Each module card shows: name, icon, description, sync status, scope (space/global)
- Enabled modules appear in the space's app switcher
- Uses existing `enabledModules` API in `server/spaces.ts`

## Multiplayer Tiers
### Tier 1 — Already multiplayer (12 modules) — just need "Pull to rSpace" button
rbooks, rcal, rcart, rfiles, rflows, rinbox, rnotes, rsocials, rsplat, rtasks, rtrips, rvote

### Tier 2 — Near-multiplayer, need Automerge integration (5 modules)
- **rchoices**: Add schema + local-first-client for voting sessions, live vote tallies
- **rswag**: Add schema for shared design state, collaborative editing
- **rwallet**: Add schema for shared wallet watchlist, collaborative treasury view
- **rschedule**: Already has schemas, needs local-first-client.ts + component sync
- **rnetwork**: Already has WebSocket, add Automerge doc for CRM data persistence

### Tier 3 — UI-only wrappers, add lightweight sync (4 modules)
- **rdata**: Sync dashboard config/filters across participants
- **rphotos**: Sync album curation, shared selections
- **rtube**: Sync playlists, watch parties, queue state
- **rpubs**: Sync publication drafts, collaborative editing queue

### Tier 4 — External service wrappers, iframe-based (3 modules)
- **rdesign** (Affine): Add space-scoped project linking, cannot sync internal state
- **rdocs** (Docmost): Add space-scoped doc linking
- **rmeets** (Jitsi): Add meeting history/scheduling sync

### Tier 5 — Infrastructure, minimal sync needed (3 modules)
- **rforum**: Provision state only, sync forum URL/status per space
- **rmaps**: Already has ephemeral WebSocket rooms — add persistent map annotations via Automerge
- **rspace**: Core module — canvas state already synced via Automerge in host app

## Architecture Decisions
- All new local-first clients follow the established pattern: `local-first-client.ts` + `schemas.ts` per module
- Document ID format: `{space}:{module}:{collection}`
- "Pull to rSpace" UI reuses existing `PATCH /:space/modules` API
- Shared `folk-applet-catalog.ts` component renders the catalog modal
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Every rApp module has real-time multiplayer sync or a clear reason why not (external iframe wrappers)
- [ ] #2 Standard 'Pull rApplet to rSpace' UI exists in space settings and is accessible from app switcher
- [ ] #3 Space owners can enable/disable any module via the catalog UI
- [ ] #4 All new sync follows established local-first-client.ts + schemas.ts pattern
- [ ] #5 Demo/unauthenticated mode still works as local-only fallback for all modules
<!-- AC:END -->

## Implementation Notes

<!-- SECTION:NOTES:BEGIN -->
All 14 sub-tasks complete. Every rApp module now has schemas.ts + local-first-client.ts for Automerge CRDT sync. Key modules (rchoices, rswag, rwallet) have full UI integration with LIVE indicators and real-time sync.
<!-- SECTION:NOTES:END -->
