# rSpace — Collaborative Canvas Platform **Module ID:** `rspace` **Domain:** `rspace.online` **Version:** 0.1.0 **Framework:** Bun / Hono / Automerge CRDT / Vite **Status:** Active ## Purpose Real-time collaborative canvas where groups can create, arrange, and interact with shapes representing different tools — notes, polls, maps, budgets, itineraries. The foundation layer for the r*.online ecosystem. Subdomain-based routing creates ephemeral or persistent community spaces. ## Data Model ### CRDT Document (Automerge) ```typescript interface CommunityDoc { meta: CommunityMeta; shapes: { [id: string]: ShapeData }; } interface CommunityMeta { name: string; slug: string; createdAt: string; visibility: SpaceVisibility; ownerDID: string | null; } interface ShapeData { type: string; // 'folk-note', 'folk-itinerary', 'demo-poll', etc. id: string; x: number; y: number; width: number; height: number; rotation: number; content?: string; sourceId?: string; // For arrows/connections targetId?: string; [key: string]: unknown; // Shape-specific properties } ``` ### Storage - Primary: Automerge binary files at `data/communities/{slug}.automerge` - Fallback: JSON at `data/communities/{slug}.json` (auto-migrated) - In-memory cache with debounced saves (2s) ## Permission Model ### Space Integration - **SpaceVisibility:** All four levels (public, public_read, authenticated, members_only) - **Default role for open spaces:** PARTICIPANT (everyone can add shapes) ### Capabilities | Capability | Required SpaceRole | AuthLevel | Description | |-----------|-------------------|-----------|-------------| | `view_canvas` | VIEWER | BASIC | See canvas shapes and layout | | `add_shapes` | PARTICIPANT | STANDARD | Create new shapes on canvas | | `edit_own_shapes` | PARTICIPANT | STANDARD | Modify/delete own shapes | | `edit_any_shape` | MODERATOR | STANDARD | Modify/delete any user's shapes | | `delete_any_shape` | MODERATOR | STANDARD | Remove any shape | | `configure_space` | ADMIN | ELEVATED | Change visibility, name, settings | ### Module-Specific Overrides Currently uses a boolean `readOnly` flag on WebSocket connections: - `readOnly=true` if visibility is `public_read` and user is unauthenticated - No per-shape ownership tracking yet (all authenticated users can edit all shapes) ### Current Auth Implementation - EncryptID token verification via `verifyEncryptIDToken()` - `evaluateSpaceAccess()` at WebSocket upgrade and HTTP routes - `extractToken()` from Authorization header - `authenticateWSUpgrade()` for WebSocket connections ## API Endpoints ### HTTP Routes | Method | Path | Auth Required | Capability | Description | |--------|------|---------------|------------|-------------| | POST | /api/communities | Yes | configure_space | Create community | | GET | /api/communities/:slug | Depends | view_canvas | Get community metadata | | GET | /api/communities/:slug/shapes | Depends | view_canvas | Export shapes as JSON | | POST | /api/communities/:slug/shapes | Yes | add_shapes | Add shapes via API | | POST | /api/communities/demo/reset | — | — | Reset demo data (rate-limited) | ### WebSocket Protocol | Message Type | Direction | Purpose | |-------------|-----------|---------| | `sync` | Bidirectional | Automerge binary sync messages | | `snapshot` | Server→Client | Full JSON state (JSON mode clients) | | `presence` | Bidirectional | Cursor/user presence updates | | `ping`/`pong` | Bidirectional | Keep-alive heartbeat | | `update` | Client→Server | Legacy shape update (JSON mode) | | `delete` | Client→Server | Legacy shape deletion (JSON mode) | | `error` | Server→Client | Auth/permission errors | **Dual sync modes:** Automerge (binary) for rich clients, JSON snapshot for simple clients. ## Canvas Integration rSpace IS the canvas. Other modules embed as shapes: | Shape Type | Module | Description | |-----------|--------|-------------| | `demo-poll` | rVote | Embedded voting widget | | `folk-note` | rNotes | Editable note card | | `folk-itinerary` | rTrips | Trip timeline | | `folk-destination` | rMaps | Map location marker | | `folk-budget` | rFunds | Budget tracker | | `demo-expense` | rFunds | Expense with split tracking | | `demo-cart-item` | rCart | Shopping item | | `folk-token-mint` | rWallet | Token issuance | | `folk-token-ledger` | rWallet | Token distribution | | `folk-packing-list` | rTrips | Checklist | | `folk-notebook` | rNotes | Notebook container | | `folk-arrow` | — | Visual connection between shapes | ## Cross-Module Dependencies | Module | Integration | |--------|------------| | **All modules** | Shapes from every module render on the rSpace canvas | | **EncryptID** | Identity, authentication, DID-based ownership | | **rNetwork** | Community membership graph | ## Local-First / Offline Support - **CRDT strategy:** Automerge documents. Full offline editing supported. - **Conflict resolution:** Automerge's built-in LWW semantics. No manual conflict resolution needed. - **Sync protocol:** Custom WebSocket relay. Clients exchange Automerge sync messages via the server. Server maintains per-peer sync state. - **Offline → online:** On reconnect, client sends accumulated changes. Server merges and broadcasts. ## Migration Plan 1. Add `members` map to `CommunityMeta` for inline CRDT membership 2. Replace boolean `readOnly` with `SpaceRole` in WebSocket connection data 3. Use `resolveSpaceRole()` at WS upgrade to determine role 4. Add `hasCapability()` checks in WS message handler before accepting writes 5. Add per-shape `createdBy` DID tracking for own-vs-any permission distinction 6. Add `encryptIDSpaceRoleAuth()` Hono middleware to HTTP API routes