151 lines
5.6 KiB
Markdown
151 lines
5.6 KiB
Markdown
# 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
|