From f23e07fbbaf37fed534caae98c800cae9c593434 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Tue, 17 Feb 2026 14:32:04 -0700 Subject: [PATCH] docs: add API-REFERENCE.md for rStack module endpoints Complete HTTP endpoint documentation for all 13 r*.online modules (57+ endpoints). Includes WebSocket protocols, EncryptID SDK usage examples, and authentication patterns for Hono, Next.js, and Python/Django. Co-Authored-By: Claude Opus 4.6 --- docs/API-REFERENCE.md | 792 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 792 insertions(+) create mode 100644 docs/API-REFERENCE.md diff --git a/docs/API-REFERENCE.md b/docs/API-REFERENCE.md new file mode 100644 index 0000000..416abae --- /dev/null +++ b/docs/API-REFERENCE.md @@ -0,0 +1,792 @@ +# r*.online Ecosystem — API Reference + +> Complete endpoint documentation for all modules + +**Version**: 1.0.0 +**Last Updated**: February 2026 + +--- + +## Authentication + +All modules authenticate via **EncryptID** (WebAuthn passkeys + JWT). + +### Token Acquisition + +``` +POST https://encryptid.jeffemmett.com/api/auth/start +→ { challenge, allowCredentials } + +POST https://encryptid.jeffemmett.com/api/auth/complete +← { token: "eyJ..." } +``` + +### Token Transport + +Attach the JWT to requests using one of: + +| Method | Format | Use Case | +|--------|--------|----------| +| Header | `Authorization: Bearer ` | API calls | +| Cookie | `encryptid_token=` | Browser navigation | +| Query | `?token=` | WebSocket upgrade | +| Subprotocol | `Sec-WebSocket-Protocol: encryptid.` | WebSocket upgrade | + +### JWT Claims + +```json +{ + "sub": "user_abc123", + "did": "did:key:z6Mk...", + "username": "ana", + "authLevel": 2, + "aud": ["rspace.online", "rvote.online", "rnotes.online"], + "iat": 1708000000, + "exp": 1708086400 +} +``` + +### Auth Levels + +| Level | Name | Grants | +|-------|------|--------| +| 1 | BASIC | Read-only operations | +| 2 | STANDARD | Create and edit own resources | +| 3 | ELEVATED | Moderate other users' resources | +| 4 | CRITICAL | Admin operations, key management | + +--- + +## Space Roles & Permissions + +Every request within a space is authorized against a **SpaceRole**: + +| Role | Level | Default For | +|------|-------|-------------| +| VIEWER | 0 | Anonymous users in PUBLIC_READ spaces | +| PARTICIPANT | 1 | Anonymous users in PUBLIC spaces, authenticated users in PUBLIC_READ | +| MODERATOR | 2 | Explicitly granted | +| ADMIN | 3 | Space owner, explicitly granted | + +### Checking Capabilities + +Each module defines a capability map. Example (rVote): + +```typescript +import { hasCapability, SpaceRole, RVOTE_PERMISSIONS } from '@encryptid/sdk'; + +// Returns true if user's role meets minimum for capability +hasCapability(SpaceRole.PARTICIPANT, 'cast_vote', RVOTE_PERMISSIONS); // true +hasCapability(SpaceRole.VIEWER, 'cast_vote', RVOTE_PERMISSIONS); // false +``` + +--- + +## Common Patterns + +### Error Responses + +All modules return errors in this format: + +```json +{ + "error": "Not authorized", + "code": "FORBIDDEN", + "status": 403 +} +``` + +| Status | Meaning | +|--------|---------| +| 400 | Invalid request body or parameters | +| 401 | Missing or invalid authentication token | +| 403 | Authenticated but insufficient permissions | +| 404 | Resource not found | +| 409 | Conflict (duplicate slug, concurrent edit) | +| 429 | Rate limited | +| 500 | Internal server error | + +### Pagination + +Modules using Prisma support cursor-based pagination: + +``` +GET /api/proposals?cursor=abc123&take=20 +``` + +### Space Scoping + +Most endpoints are scoped to a space via subdomain: + +``` +https://crypto.rvote.online/api/proposals → proposals in "crypto" space +https://coop.rnotes.online/api/notebooks → notebooks in "coop" space +``` + +--- + +## EncryptID Server + +**Base URL**: `https://encryptid.jeffemmett.com` +**Tech**: Hono.js on Bun | PostgreSQL + +### Registration + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/register/start` | None | Begin WebAuthn registration, returns challenge + options | +| POST | `/api/register/complete` | None | Complete registration, store credential, return JWT | + +### Authentication + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/auth/start` | None | Begin authentication, returns challenge | +| POST | `/api/auth/complete` | None | Complete authentication, return JWT | + +### Session Management + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/session/verify` | Bearer | Verify token validity, returns claims | +| POST | `/api/session/verify` | Body | Verify token passed in request body | +| POST | `/api/session/refresh` | Bearer | Refresh expired token (1h grace period) | + +### Credential Management + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/user/credentials` | Bearer | List user's registered passkeys | + +### Recovery + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/recovery/email/set` | Bearer | Set recovery email address | +| POST | `/api/recovery/email/request` | None | Request recovery email (30-min token) | +| POST | `/api/recovery/email/verify` | None | Verify recovery token, issue new session | + +### Membership (NEW — Phase 4) + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/spaces/:slug/members` | Admin | Add or update member in space | +| GET | `/api/spaces/:slug/members` | Bearer | List all members of a space | +| GET | `/api/spaces/:slug/members/:did` | Bearer | Get one member's role in space | +| DELETE | `/api/spaces/:slug/members/:did` | Admin | Remove member from space | + +--- + +## rVote — Decision Engine + +**Base URL**: `https://{space}.rvote.online` +**Tech**: Next.js App Router | PostgreSQL (Prisma) | NextAuth + +### Proposals + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/proposals` | Optional | List proposals (filterable by status) | +| POST | `/api/proposals` | PARTICIPANT | Create new proposal | +| GET | `/api/proposals/:id` | Optional | Get proposal with vote data | +| PATCH | `/api/proposals/:id` | Author | Edit proposal (before voting phase) | +| DELETE | `/api/proposals/:id` | Author | Delete proposal (before voting phase) | + +### Voting + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/proposals/:id/vote` | PARTICIPANT | Cast or update ranking vote (spend credits) | +| DELETE | `/api/proposals/:id/vote` | PARTICIPANT | Remove vote (refund credits) | +| GET | `/api/proposals/:id/final-vote` | Optional | Get final vote counts (YES/NO/ABSTAIN) | +| POST | `/api/proposals/:id/final-vote` | PARTICIPANT | Cast final vote on promoted proposal | + +### Credits + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/user/credits` | Bearer | Get user's available and stored credits | +| POST | `/api/user/credits` | Bearer | Claim accumulated daily credits | + +### Spaces + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/spaces` | Bearer | List user's spaces | +| POST | `/api/spaces` | Bearer | Create new space | +| GET | `/api/spaces/:slug` | Optional | Get space details | +| PATCH | `/api/spaces/:slug` | ADMIN | Update space settings | +| DELETE | `/api/spaces/:slug` | Owner | Delete space | + +### Members + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/spaces/:slug/members` | Bearer | List space members with roles | +| POST | `/api/spaces/:slug/members` | ADMIN | Add member by email | +| PATCH | `/api/spaces/:slug/members/:userId` | ADMIN | Update member role | +| DELETE | `/api/spaces/:slug/members/:userId` | ADMIN | Remove member | + +### Invites + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/spaces/:slug/invites` | ADMIN | List active invite links | +| POST | `/api/spaces/:slug/invites` | ADMIN | Create invite link | +| DELETE | `/api/spaces/:slug/invites/:id` | ADMIN | Revoke invite link | +| GET | `/api/spaces/join/:token` | Bearer | Join space via invite token | + +--- + +## rNotes — Collaborative Notebooks + +**Base URL**: `https://{space}.rnotes.online` +**Tech**: Next.js App Router | PostgreSQL (Prisma) | Automerge CRDT + +### Notes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/notes` | Bearer | List notes (filter by notebook, type, tags) | +| POST | `/api/notes` | PARTICIPANT | Create note | +| GET | `/api/notes/:id` | Bearer | Get note with tags | +| PATCH | `/api/notes/:id` | Author/MOD | Update note | +| DELETE | `/api/notes/:id` | Author/MOD | Delete note | +| GET | `/api/notes/search?q=` | Bearer | Full-text search across notes | + +### Notebooks + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/notebooks` | Bearer | List notebooks with note counts | +| POST | `/api/notebooks` | PARTICIPANT | Create notebook | +| GET | `/api/notebooks/:id` | Bearer | Get notebook details | +| PATCH | `/api/notebooks/:id` | Owner/MOD | Update notebook metadata | +| DELETE | `/api/notebooks/:id` | Owner/ADMIN | Delete notebook | +| GET | `/api/notebooks/:id/notes` | Bearer | Get notes in notebook | +| GET | `/api/notebooks/:id/canvas` | Bearer | Get canvas shape data for notebook | + +### File Uploads + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/uploads` | Bearer | Upload file (max 50MB) | +| GET | `/api/uploads/:filename` | Bearer | Download file | + +### Voice + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/voice/transcribe` | Bearer | Transcribe audio file | +| POST | `/api/voice/diarize` | Bearer | Speaker diarization | + +### Canvas Sync + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/sync` | Service | Receive shape updates from rSpace canvas | + +--- + +## rSpace — Collaborative Canvas + +**Base URL**: `https://{space}.rspace.online` +**Tech**: Hono.js on Bun | Automerge CRDT | WebSocket + +### WebSocket Protocol + +Connect: `wss://{space}.rspace.online/?mode=automerge&token=JWT` + +| Message Type | Direction | Payload | +|--------------|-----------|---------| +| `sync` | Bidirectional | Automerge binary sync frame | +| `snapshot` | Server→Client | Full JSON state (fallback mode) | +| `presence` | Bidirectional | `{ userId, cursor, selection }` | +| `ping` | Client→Server | Keep-alive (30s interval) | +| `pong` | Server→Client | Keep-alive response | +| `error` | Server→Client | `{ code, message }` | + +### REST API + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/health` | None | Health check with DB connectivity | +| GET | `/api/communities/:slug` | Optional | Get community metadata | +| POST | `/api/communities/:slug/shapes/:id` | Service | Update shape from module callback | + +### Shape Update API (NEW — bidirectional sync) + +```json +POST /api/communities/{slug}/shapes/{shapeId} +Authorization: Bearer + +{ + "title": "Updated Note Title", + "status": "PASSED", + "fields": { ... } +} +``` + +--- + +## rCal — Calendar & Scheduling + +**Base URL**: `https://{space}.rcal.online` +**Tech**: Next.js App Router | PostgreSQL (Prisma) + +### Events + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/events` | Optional | List events (filter by date range, source) | +| POST | `/api/events` | PARTICIPANT | Create event | +| GET | `/api/events/:id` | Optional | Get event details | +| PATCH | `/api/events/:id` | Author/MOD | Update event | +| DELETE | `/api/events/:id` | Author/MOD | Delete event | + +### Calendar Sources + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/sources` | Bearer | List calendar sources (Google, iCal, etc.) | +| POST | `/api/sources` | PARTICIPANT | Add calendar source | +| GET | `/api/sources/:id` | Bearer | Get source details | +| PATCH | `/api/sources/:id` | Owner | Update source config | +| DELETE | `/api/sources/:id` | Owner | Remove source | + +### Context & Integration + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/context/:tool` | Bearer | Get events relevant to an r-tool context | +| GET | `/api/lunar` | None | Get lunar phase data for date range | + +--- + +## rMaps — Spatial Intelligence + +**Base URL**: `https://{space}.rmaps.online` +**Tech**: Next.js App Router | PostgreSQL (Prisma) | MapLibre GL + +### Routing + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/routing` | Optional | Calculate route (origin, dest, mode) | + +Request body: +```json +{ + "origin": { "lat": 52.52, "lng": 13.405 }, + "destination": { "lat": 52.51, "lng": 13.39 }, + "mode": "walking", + "indoor": false +} +``` + +### Indoor Maps (c3nav) + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/c3nav/:event` | None | Get indoor map metadata for event | +| GET | `/api/c3nav/tiles/:event/:level/:z/:x/:y` | None | Tile server for indoor maps | + +### WebSocket (Real-time Location Sharing) + +Connect: `wss://{room}.rmaps.online/ws?token=JWT` + +| Message Type | Direction | Payload | +|--------------|-----------|---------| +| `location` | Client→Server | `{ lat, lng, accuracy, privacy }` | +| `locations` | Server→Client | `{ participants: [...] }` | +| `waypoint` | Bidirectional | `{ id, name, lat, lng, emoji }` | + +Privacy modes: `exact`, `approximate`, `area`, `ghost` + +--- + +## rFiles — Secure File Sharing + +**Base URL**: `https://{space}.rfiles.online` +**Tech**: Django REST Framework | PostgreSQL | S3-compatible storage + +### Media Files + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/v1/media/` | Bearer | List files (filter by mime, tags, space) | +| POST | `/api/v1/media/` | PARTICIPANT | Upload file | +| GET | `/api/v1/media/:id/` | Bearer | Get file metadata | +| PATCH | `/api/v1/media/:id/` | Author/MOD | Update metadata | +| DELETE | `/api/v1/media/:id/` | Author/ADMIN | Delete file | +| POST | `/api/v1/media/:id/share/` | Author | Create share link | +| GET | `/api/v1/media/:id/shares/` | Author | List shares for file | +| GET | `/api/v1/media/:id/access_logs/` | Author | Access history (last 100) | + +### Public Shares + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/s/:token/` | None | Download shared file | +| GET | `/s/:token/download/` | None | Explicit download | +| GET | `/s/:token/info/` | None | Get share info (no download) | +| POST | `/s/:token/verify/` | None | Verify password-protected share | + +### Share Management + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/v1/shares/` | Bearer | List user's shares | +| POST | `/api/v1/shares/:id/revoke/` | Author | Revoke share link | +| POST | `/api/v1/shares/:id/set_password/` | Author | Add password to share | +| POST | `/api/v1/shares/:id/remove_password/` | Author | Remove password | + +### Direct Upload + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/upload/` | Bearer | Direct file upload (FormData or pre-signed) | + +--- + +## rFunds — Funding Flows + +**Base URL**: `https://{space}.rfunds.online` +**Tech**: Next.js | React Flow | Automerge CRDT + +rFunds is primarily client-side (Automerge + localStorage). The flow diagrams are stored in the rSpace Automerge document as `folk-budget` and `demo-expense` shapes. + +### Planned Server API + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/flows` | PARTICIPANT | Save flow diagram to server | +| GET | `/api/flows/:id` | VIEWER | Get flow diagram | +| POST | `/api/campaigns` | PARTICIPANT | Create crowdfunding campaign | +| POST | `/api/campaigns/:id/contribute` | PARTICIPANT | Contribute to campaign | + +--- + +## rTube — Community Video + +**Base URL**: `https://{space}.rtube.online` +**Tech**: Hono.js on Bun | Cloudflare R2 + +### Videos + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/upload` | PARTICIPANT | Upload video file | +| GET | `/api/videos` | VIEWER | List videos in space | +| GET | `/api/videos/:id` | VIEWER | Get video metadata | +| GET | `/api/videos/:id/stream` | VIEWER | Stream video (HTTP range requests) | + +### Live Streaming (Planned) + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/streams` | PARTICIPANT | Start RTMP ingest stream | +| GET | `/api/streams/:id/hls` | VIEWER | Get HLS playlist for live stream | + +--- + +## rMail — Secure Messaging + +**Base URL**: `https://{space}.rmail.online` +**Tech**: Next.js App Router | PostgreSQL (Prisma) | IMAP + +### Mailboxes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/mailboxes` | Bearer | List user's mailboxes | +| POST | `/api/mailboxes` | ADMIN | Create mailbox (IMAP/SMTP config) | +| GET | `/api/mailboxes/:slug/members` | Bearer | List mailbox members | +| POST | `/api/mailboxes/:slug/members` | ADMIN | Add member | +| DELETE | `/api/mailboxes/:slug/members/:did` | ADMIN | Remove member | + +### Messages + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/mailboxes/:slug/messages` | Bearer | List messages (IMAP sync) | +| GET | `/api/mailboxes/:slug/threads/:threadId` | Bearer | Get email thread | +| PATCH | `/api/mailboxes/:slug/threads/:threadId` | Bearer | Update thread (read, folder) | +| POST | `/api/mailboxes/:slug/threads/:threadId/comments` | Bearer | Add comment | + +### Multi-Sig Approvals + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/approvals` | Bearer | List pending approvals | +| POST | `/api/approvals` | PARTICIPANT | Create email approval draft | +| GET | `/api/approvals/:id` | Bearer | Get approval with signatures | +| POST | `/api/approvals/:id/sign` | Bearer | Sign approval (add signature) | + +### Workspaces + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/workspaces` | Bearer | List workspaces | +| POST | `/api/workspaces` | Bearer | Create workspace | +| GET | `/api/workspaces/:slug/mailboxes` | Bearer | List mailboxes in workspace | + +--- + +## rTrips — Trip Planning + +**Base URL**: `https://{space}.rtrips.online` +**Tech**: Next.js App Router | PostgreSQL (Prisma) | Gemini AI + +### Trips + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/trips` | Bearer | List trips | +| POST | `/api/trips` | PARTICIPANT | Create trip from parsed text | +| GET | `/api/trips/:id` | Bearer | Get trip with all entities | +| PATCH | `/api/trips/:id` | Author/MOD | Update trip | +| DELETE | `/api/trips/:id` | Author/ADMIN | Delete trip | +| GET | `/api/trips/by-slug/:slug` | Bearer | Get trip by slug | +| POST | `/api/trips/parse` | Bearer | AI-parse trip text → structured data | + +### Destinations + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/trips/:id/destinations` | Bearer | List destinations | +| POST | `/api/trips/:id/destinations` | PARTICIPANT | Add destination | +| PATCH | `/api/trips/:id/destinations/:destId` | Author/MOD | Update destination | +| DELETE | `/api/trips/:id/destinations/:destId` | Author/MOD | Remove destination | + +### Itinerary + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/trips/:id/itinerary` | Bearer | List itinerary items | +| POST | `/api/trips/:id/itinerary` | PARTICIPANT | Add item | +| PATCH | `/api/trips/:id/itinerary/:itemId` | Author/MOD | Update item | +| DELETE | `/api/trips/:id/itinerary/:itemId` | Author/MOD | Remove item | + +### Bookings + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/trips/:id/bookings` | Bearer | List bookings | +| POST | `/api/trips/:id/bookings` | PARTICIPANT | Add booking | +| PATCH | `/api/trips/:id/bookings/:bookingId` | Author/MOD | Update booking | +| DELETE | `/api/trips/:id/bookings/:bookingId` | Author/MOD | Cancel booking | + +### Expenses + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/trips/:id/expenses` | Bearer | List expenses | +| POST | `/api/trips/:id/expenses` | PARTICIPANT | Add expense | +| PATCH | `/api/trips/:id/expenses/:expenseId` | Author/MOD | Update expense | +| DELETE | `/api/trips/:id/expenses/:expenseId` | Author/MOD | Remove expense | + +### Packing Lists + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/trips/:id/packing` | Bearer | Get packing list | +| POST | `/api/trips/:id/packing` | PARTICIPANT | Add item | +| PATCH | `/api/trips/:id/packing/:itemId` | Bearer | Check off item | +| DELETE | `/api/trips/:id/packing/:itemId` | Author/MOD | Remove item | + +### Canvas & Cross-Module + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/trips/:id/canvas` | Bearer | Get linked canvas shape data | +| POST | `/api/trips/:id/sync` | Service | Receive canvas shape updates | +| POST | `/api/proxy/rvote` | Bearer | Proxy to rVote (in-trip voting) | +| POST | `/api/proxy/rnotes` | Bearer | Proxy to rNotes (shared trip notes) | + +--- + +## rNetwork — Social Graph + +**Base URL**: `https://{space}.rnetwork.online` +**Tech**: Next.js | Automerge CRDT | Force-directed graph + +rNetwork uses Automerge CRDT for the graph document. No traditional REST API — state syncs via WebSocket. + +### WebSocket Protocol + +Connect: `wss://{space}.rnetwork.online/ws?token=JWT` + +| Message Type | Direction | Payload | +|--------------|-----------|---------| +| `sync` | Bidirectional | Automerge binary sync frame | +| `snapshot` | Server→Client | Full graph JSON | + +### Graph Document Schema + +```typescript +{ + meta: { name, slug, visibility, ownerDID }, + nodes: { + [id]: { type: "person"|"org"|"role", name, metadata } + }, + edges: { + [id]: { source, target, type, label, metadata } + } +} +``` + +--- + +## rWallet — Treasury + +**Base URL**: `https://{space}.rwallet.online` +**Tech**: Next.js | Gnosis Safe SDK + +Currently static frontend. Planned API: + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/safes` | Bearer | List connected Safe wallets | +| GET | `/api/safes/:address/balances` | VIEWER | Get token balances | +| GET | `/api/safes/:address/transactions` | VIEWER | List transactions | +| POST | `/api/safes/:address/propose` | PARTICIPANT | Propose new transaction | +| POST | `/api/safes/:address/sign/:txHash` | PARTICIPANT | Sign pending transaction | + +--- + +## EncryptID SDK (npm: `@encryptid/sdk`) + +### Client-Side + +```typescript +import { EncryptIDClient } from '@encryptid/sdk/client'; + +const client = new EncryptIDClient('https://encryptid.jeffemmett.com'); + +// Register +await client.register(username); + +// Authenticate +const { token, claims } = await client.authenticate(); + +// Share token across r*.online modules +await client.shareTokenAcrossModules(token, [ + 'rvote.online', + 'rnotes.online', + 'rmaps.online', +]); +``` + +### Server-Side (Hono) + +```typescript +import { + encryptIDAuth, + encryptIDSpaceRoleAuth, +} from '@encryptid/sdk/server/middleware/hono'; + +// Simple auth (require valid token) +app.use('/api/*', encryptIDAuth({ serverUrl })); + +// Space + role auth (combined) +app.use('/api/*', encryptIDSpaceRoleAuth({ serverUrl, lookupMembership })); + +// Access context +app.get('/api/data', (c) => { + const claims = c.get('encryptid'); // EncryptIDClaims + const role = c.get('spaceRole'); // ResolvedRole + return c.json({ user: claims.did, role: role.role }); +}); +``` + +### Server-Side (Next.js) + +```typescript +import { + checkSpaceRole, + withSpaceRole, +} from '@encryptid/sdk/server/middleware/nextjs'; + +// In a route handler +export async function GET(req) { + const { claims, role } = await checkSpaceRole(req, { + serverUrl: 'https://encryptid.jeffemmett.com', + spaceSlug: 'crypto', + }); + if (!hasCapability(role.role, 'view_proposals', RVOTE_PERMISSIONS)) { + return Response.json({ error: 'Forbidden' }, { status: 403 }); + } + // ... handle request +} +``` + +### Server-Side (Python/Django) + +```python +from encryptid.roles import SpaceRole, has_capability, RFILES_PERMISSIONS + +# Check if user can upload files +if has_capability(user_role, 'upload_file', RFILES_PERMISSIONS): + # allow upload +``` + +### Capability Maps + +Import per-module permission maps: + +```typescript +import { RVOTE_PERMISSIONS } from '@encryptid/sdk/types/modules'; +import { RSPACE_PERMISSIONS } from '@encryptid/sdk/types/modules'; +import { RNOTES_PERMISSIONS } from '@encryptid/sdk/types/modules'; +import { RFUNDS_PERMISSIONS } from '@encryptid/sdk/types/modules'; +import { RMAPS_PERMISSIONS } from '@encryptid/sdk/types/modules'; +import { RTUBE_PERMISSIONS } from '@encryptid/sdk/types/modules'; +``` + +--- + +## WebSocket Protocols Summary + +| Module | URL Pattern | Protocol | Auth | +|--------|-------------|----------|------| +| rSpace | `wss://{slug}.rspace.online/` | Automerge binary sync | JWT (query/subprotocol) | +| rNetwork | `wss://{slug}.rnetwork.online/ws` | Automerge binary sync | JWT (query/subprotocol) | +| rMaps | `wss://{room}.rmaps.online/ws` | JSON location/waypoint | JWT (query) | + +--- + +## Cross-Module Integration + +### Canvas Shape Sync (module → canvas) + +When a module updates an entity bound to a canvas shape: + +``` +POST https://{slug}.rspace.online/api/communities/{slug}/shapes/{shapeId} +Authorization: Bearer +Content-Type: application/json + +{ + "title": "Updated Title", + "status": "PASSED" +} +``` + +### Canvas Shape Sync (canvas → module) + +When rSpace updates a shape bound to a module entity, it calls the module's sync endpoint: + +``` +POST https://{slug}.rnotes.online/api/sync +POST https://{slug}.rtrips.online/api/trips/{id}/sync +``` + +### Module Proxying + +rTrips demonstrates the proxy pattern for embedded module access: + +``` +POST /api/proxy/rvote → proxied to rvote.online +POST /api/proxy/rnotes → proxied to rnotes.online +```