From e4bcc3f04a6b63b2cbd93c8067063c73847766e0 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Tue, 17 Feb 2026 12:30:14 -0700 Subject: [PATCH] docs: add MODULE_SPEC.md with permission model and capabilities Co-Authored-By: Claude Opus 4.6 --- MODULE_SPEC.md | 150 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 MODULE_SPEC.md diff --git a/MODULE_SPEC.md b/MODULE_SPEC.md new file mode 100644 index 0000000..e4dff93 --- /dev/null +++ b/MODULE_SPEC.md @@ -0,0 +1,150 @@ +# 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