# 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
