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 <noreply@anthropic.com>
This commit is contained in:
parent
d4931b0ef2
commit
f23e07fbba
|
|
@ -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 <token>` | API calls |
|
||||||
|
| Cookie | `encryptid_token=<token>` | Browser navigation |
|
||||||
|
| Query | `?token=<token>` | WebSocket upgrade |
|
||||||
|
| Subprotocol | `Sec-WebSocket-Protocol: encryptid.<token>` | 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 <service-jwt>
|
||||||
|
|
||||||
|
{
|
||||||
|
"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 <service-jwt>
|
||||||
|
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
|
||||||
|
```
|
||||||
Loading…
Reference in New Issue