@xtr-dev/rondevu-server 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@xtr-dev/rondevu-server)](https://www.npmjs.com/package/@xtr-dev/rondevu-server)
4
4
 
5
- 🌐 **DNS-like WebRTC signaling with username claiming and service discovery**
5
+ 🌐 **Simple WebRTC signaling with RPC interface**
6
6
 
7
- Scalable WebRTC signaling server with cryptographic username claiming, service publishing, and privacy-preserving discovery.
7
+ Scalable WebRTC signaling server with cryptographic username claiming, service publishing with semantic versioning, and efficient offer/answer exchange via JSON-RPC interface.
8
8
 
9
9
  **Related repositories:**
10
10
  - [@xtr-dev/rondevu-client](https://github.com/xtr-dev/rondevu-client) - TypeScript client library ([npm](https://www.npmjs.com/package/@xtr-dev/rondevu-client))
@@ -15,12 +15,14 @@ Scalable WebRTC signaling server with cryptographic username claiming, service p
15
15
 
16
16
  ## Features
17
17
 
18
+ - **RPC Interface**: Single endpoint for all operations with batching support
18
19
  - **Username Claiming**: Cryptographic username ownership with Ed25519 signatures (365-day validity, auto-renewed on use)
19
- - **Service Publishing**: Package-style naming with semantic versioning (com.example.chat@1.0.0)
20
- - **Privacy-Preserving Discovery**: UUID-based service index prevents enumeration
21
- - **Public/Private Services**: Control service visibility
22
- - **Stateless Authentication**: AES-256-GCM encrypted credentials, no server-side sessions
20
+ - **Service Publishing**: Service:version@username naming (e.g., `chat:1.0.0@alice`)
21
+ - **Service Discovery**: Random and paginated discovery for finding services without knowing usernames
22
+ - **Semantic Versioning**: Compatible version matching (chat:1.0.0 matches any 1.x.x)
23
+ - **Signature-Based Authentication**: All authenticated requests use Ed25519 signatures
23
24
  - **Complete WebRTC Signaling**: Offer/answer exchange and ICE candidate relay
25
+ - **Batch Operations**: Execute multiple operations in a single HTTP request
24
26
  - **Dual Storage**: SQLite (Node.js/Docker) and Cloudflare D1 (Workers) backends
25
27
 
26
28
  ## Architecture
@@ -30,11 +32,13 @@ Username Claiming → Service Publishing → Service Discovery → WebRTC Connec
30
32
 
31
33
  alice claims "alice" with Ed25519 signature
32
34
 
33
- alice publishes com.example.chat@1.0.0 with multiple offers → receives UUID abc123
35
+ alice publishes chat:1.0.0@alice with offers
34
36
 
35
- bob requests alice/com.example.chat@1.0.0 → gets compatible service with available offer
37
+ bob queries chat:1.0.0@alice (direct) or chat:1.0.0 (discovery) → gets offer SDP
36
38
 
37
- WebRTC connection established via offer/answer exchange
39
+ bob posts answer SDP → WebRTC connection established
40
+
41
+ ICE candidates exchanged via server relay
38
42
  ```
39
43
 
40
44
  ## Quick Start
@@ -46,7 +50,7 @@ npm install && npm start
46
50
 
47
51
  **Docker:**
48
52
  ```bash
49
- docker build -t rondevu . && docker run -p 3000:3000 -e STORAGE_PATH=:memory: -e AUTH_SECRET=$(openssl rand -hex 32) rondevu
53
+ docker build -t rondevu . && docker run -p 3000:3000 -e STORAGE_PATH=:memory: rondevu
50
54
  ```
51
55
 
52
56
  **Cloudflare Workers:**
@@ -54,317 +58,201 @@ docker build -t rondevu . && docker run -p 3000:3000 -e STORAGE_PATH=:memory: -e
54
58
  npx wrangler deploy
55
59
  ```
56
60
 
57
- ## API Endpoints
58
-
59
- ### Public Endpoints
60
-
61
- #### `GET /`
62
- Returns server version and info
63
-
64
- #### `GET /health`
65
- Health check endpoint with version
61
+ ## RPC Interface
66
62
 
67
- #### `POST /register`
68
- Register a new peer and receive credentials (peerId + secret)
63
+ All API calls are made to `POST /rpc` with JSON-RPC format.
69
64
 
70
- Generates a cryptographically random 128-bit peer ID.
65
+ ### Request Format
71
66
 
72
- **Response:**
67
+ **Single method call:**
73
68
  ```json
74
69
  {
75
- "peerId": "f17c195f067255e357232e34cf0735d9",
76
- "secret": "DdorTR8QgSn9yngn+4qqR8cs1aMijvX..."
70
+ "method": "getUser",
71
+ "message": "getUser:alice:1733404800000",
72
+ "signature": "base64-encoded-signature",
73
+ "params": {
74
+ "username": "alice"
75
+ }
77
76
  }
78
77
  ```
79
78
 
80
- ### User Management (RESTful)
81
-
82
- #### `GET /users/:username`
83
- Check username availability and claim status
84
-
85
- **Response:**
79
+ **Batch calls:**
86
80
  ```json
87
- {
88
- "username": "alice",
89
- "available": false,
90
- "claimedAt": 1733404800000,
91
- "expiresAt": 1765027200000,
92
- "publicKey": "..."
93
- }
81
+ [
82
+ {
83
+ "method": "getUser",
84
+ "message": "getUser:alice:1733404800000",
85
+ "signature": "base64-encoded-signature",
86
+ "params": { "username": "alice" }
87
+ },
88
+ {
89
+ "method": "claimUsername",
90
+ "message": "claim:bob:1733404800000",
91
+ "signature": "base64-encoded-signature",
92
+ "params": {
93
+ "username": "bob",
94
+ "publicKey": "base64-encoded-public-key"
95
+ }
96
+ }
97
+ ]
94
98
  ```
95
99
 
96
- #### `POST /users/:username`
97
- Claim a username with cryptographic proof
100
+ ### Response Format
98
101
 
99
- **Request:**
102
+ **Single response:**
100
103
  ```json
101
104
  {
102
- "publicKey": "base64-encoded-ed25519-public-key",
103
- "signature": "base64-encoded-signature",
104
- "message": "claim:alice:1733404800000"
105
+ "success": true,
106
+ "result": { /* method-specific data */ }
105
107
  }
106
108
  ```
107
109
 
108
- **Response:**
110
+ **Error response:**
109
111
  ```json
110
112
  {
111
- "username": "alice",
112
- "claimedAt": 1733404800000,
113
- "expiresAt": 1765027200000
113
+ "success": false,
114
+ "error": "Error message"
114
115
  }
115
116
  ```
116
117
 
117
- **Validation:**
118
- - Username format: `^[a-z0-9][a-z0-9-]*[a-z0-9]$` (3-32 characters)
119
- - Signature must be valid Ed25519 signature
120
- - Timestamp must be within 5 minutes (replay protection)
121
- - Expires after 365 days, auto-renewed on use
118
+ **Batch responses:** Array of responses matching request array order.
122
119
 
123
- #### `GET /users/:username/services/:fqn`
124
- Get service by username and FQN with semver-compatible matching
120
+ ## Core Methods
125
121
 
126
- **Semver Matching:**
127
- - Requesting `chat@1.0.0` matches any `1.x.x` version
128
- - Major version must match exactly (`chat@1.0.0` will NOT match `chat@2.0.0`)
129
- - For major version 0, minor must also match (`0.1.0` will NOT match `0.2.0`)
130
- - Returns the most recently published compatible version
122
+ ### Username Management
131
123
 
132
- **Response:**
133
- ```json
124
+ ```typescript
125
+ // Check username availability
126
+ POST /rpc
134
127
  {
135
- "uuid": "abc123",
136
- "serviceId": "service-id",
137
- "username": "alice",
138
- "serviceFqn": "chat.app@1.0.0",
139
- "offerId": "offer-hash",
140
- "sdp": "v=0...",
141
- "isPublic": true,
142
- "metadata": {},
143
- "createdAt": 1733404800000,
144
- "expiresAt": 1733405100000
128
+ "method": "getUser",
129
+ "params": { "username": "alice" }
145
130
  }
146
- ```
147
131
 
148
- **Note:** Returns a single available offer from the service. If all offers are in use, returns 503.
149
-
150
- ### Service Management (RESTful)
151
-
152
- #### `POST /users/:username/services`
153
- Publish a service with multiple offers (requires authentication and username signature)
154
-
155
- **Headers:**
156
- - `Authorization: Bearer {peerId}:{secret}`
157
-
158
- **Request:**
159
- ```json
132
+ // Claim username (requires signature)
133
+ POST /rpc
160
134
  {
161
- "serviceFqn": "com.example.chat@1.0.0",
162
- "offers": [
163
- { "sdp": "v=0..." },
164
- { "sdp": "v=0..." }
165
- ],
166
- "ttl": 300000,
167
- "isPublic": false,
168
- "metadata": { "description": "Chat service" },
169
- "signature": "base64-encoded-signature",
170
- "message": "publish:alice:com.example.chat@1.0.0:1733404800000"
135
+ "method": "claimUsername",
136
+ "message": "claim:alice:1733404800000",
137
+ "signature": "base64-signature",
138
+ "params": {
139
+ "username": "alice",
140
+ "publicKey": "base64-public-key"
141
+ }
171
142
  }
172
143
  ```
173
144
 
174
- **Response (Full service details):**
175
- ```json
145
+ ### Service Publishing
146
+
147
+ ```typescript
148
+ // Publish service (requires signature)
149
+ POST /rpc
176
150
  {
177
- "uuid": "uuid-v4-for-index",
178
- "serviceId": "uuid-v4",
179
- "username": "alice",
180
- "serviceFqn": "com.example.chat@1.0.0",
181
- "offers": [
182
- {
183
- "offerId": "offer-hash-1",
184
- "sdp": "v=0...",
185
- "createdAt": 1733404800000,
186
- "expiresAt": 1733405100000
187
- },
188
- {
189
- "offerId": "offer-hash-2",
190
- "sdp": "v=0...",
191
- "createdAt": 1733404800000,
192
- "expiresAt": 1733405100000
193
- }
194
- ],
195
- "isPublic": false,
196
- "metadata": { "description": "Chat service" },
197
- "createdAt": 1733404800000,
198
- "expiresAt": 1733405100000
151
+ "method": "publishService",
152
+ "message": "publishService:alice:chat:1.0.0@alice:1733404800000",
153
+ "signature": "base64-signature",
154
+ "params": {
155
+ "serviceFqn": "chat:1.0.0@alice",
156
+ "offers": [{ "sdp": "webrtc-offer-sdp" }],
157
+ "ttl": 300000
158
+ }
199
159
  }
200
160
  ```
201
161
 
202
- **Service FQN Format:**
203
- - Service name: Reverse domain notation (e.g., `com.example.chat`)
204
- - Version: Semantic versioning (e.g., `1.0.0`, `2.1.3-beta`)
205
- - Complete FQN: `service-name@version` (e.g., `com.example.chat@1.0.0`)
206
-
207
- **Validation:**
208
- - Service name pattern: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$`
209
- - Length: 3-128 characters
210
- - Version pattern: `^[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9.-]+)?$`
211
-
212
- #### `GET /services/:uuid`
213
- Get service details by UUID
162
+ ### Service Discovery
214
163
 
215
- **Response:**
216
- ```json
164
+ ```typescript
165
+ // Get specific service
166
+ POST /rpc
217
167
  {
218
- "serviceId": "...",
219
- "username": "alice",
220
- "serviceFqn": "com.example.chat@1.0.0",
221
- "offerId": "...",
222
- "sdp": "v=0...",
223
- "isPublic": false,
224
- "metadata": { ... },
225
- "createdAt": 1733404800000,
226
- "expiresAt": 1733405100000
168
+ "method": "getService",
169
+ "params": { "serviceFqn": "chat:1.0.0@alice" }
227
170
  }
228
- ```
229
-
230
- #### `DELETE /users/:username/services/:fqn`
231
- Unpublish a service (requires authentication and ownership)
232
171
 
233
- **Headers:**
234
- - `Authorization: Bearer {peerId}:{secret}`
235
-
236
- **Request:**
237
- ```json
172
+ // Random discovery
173
+ POST /rpc
238
174
  {
239
- "username": "alice"
175
+ "method": "getService",
176
+ "params": { "serviceFqn": "chat:1.0.0" }
240
177
  }
241
- ```
242
178
 
243
- ### Offer Management (Low-level)
244
-
245
- #### `POST /offers`
246
- Create one or more offers (requires authentication)
247
-
248
- **Headers:**
249
- - `Authorization: Bearer {peerId}:{secret}`
250
-
251
- **Request:**
252
- ```json
179
+ // Paginated discovery
180
+ POST /rpc
253
181
  {
254
- "offers": [
255
- {
256
- "sdp": "v=0...",
257
- "ttl": 300000
258
- }
259
- ]
182
+ "method": "getService",
183
+ "params": {
184
+ "serviceFqn": "chat:1.0.0",
185
+ "limit": 10,
186
+ "offset": 0
187
+ }
260
188
  }
261
189
  ```
262
190
 
263
- #### `GET /offers/mine`
264
- List all offers owned by authenticated peer
265
-
266
- #### `DELETE /offers/:offerId`
267
- Delete a specific offer
191
+ ### WebRTC Signaling
268
192
 
269
- #### `POST /offers/:offerId/answer`
270
- Answer an offer (locks it to answerer)
271
-
272
- **Request:**
273
- ```json
193
+ ```typescript
194
+ // Answer offer (requires signature)
195
+ POST /rpc
274
196
  {
275
- "sdp": "v=0..."
197
+ "method": "answerOffer",
198
+ "message": "answer:bob:offer-id:1733404800000",
199
+ "signature": "base64-signature",
200
+ "params": {
201
+ "serviceFqn": "chat:1.0.0@alice",
202
+ "offerId": "offer-id",
203
+ "sdp": "webrtc-answer-sdp"
204
+ }
276
205
  }
277
- ```
278
206
 
279
- #### `GET /offers/:offerId/answer`
280
- Get answer for a specific offer
281
-
282
- #### `POST /offers/:offerId/ice-candidates`
283
- Post ICE candidates for an offer
207
+ // Add ICE candidates (requires signature)
208
+ POST /rpc
209
+ {
210
+ "method": "addIceCandidates",
211
+ "params": {
212
+ "serviceFqn": "chat:1.0.0@alice",
213
+ "offerId": "offer-id",
214
+ "candidates": [{ /* RTCIceCandidateInit */ }]
215
+ }
216
+ }
284
217
 
285
- **Request:**
286
- ```json
218
+ // Poll for answers and ICE candidates (requires signature)
219
+ POST /rpc
287
220
  {
288
- "candidates": ["candidate:1 1 UDP..."]
221
+ "method": "poll",
222
+ "params": { "since": 1733404800000 }
289
223
  }
290
224
  ```
291
225
 
292
- #### `GET /offers/:offerId/ice-candidates?since=1234567890`
293
- Get ICE candidates from the other peer
294
-
295
226
  ## Configuration
296
227
 
297
- Environment variables:
228
+ Quick reference for common environment variables:
298
229
 
299
230
  | Variable | Default | Description |
300
231
  |----------|---------|-------------|
301
232
  | `PORT` | `3000` | Server port (Node.js/Docker) |
302
233
  | `CORS_ORIGINS` | `*` | Comma-separated allowed origins |
303
234
  | `STORAGE_PATH` | `./rondevu.db` | SQLite database path (use `:memory:` for in-memory) |
304
- | `VERSION` | `2.0.0` | Server version (semver) |
305
- | `AUTH_SECRET` | Random 32-byte hex | Secret key for credential encryption (required for production) |
306
- | `OFFER_DEFAULT_TTL` | `300000` | Default offer TTL in ms (5 minutes) |
307
- | `OFFER_MIN_TTL` | `60000` | Minimum offer TTL in ms (1 minute) |
308
- | `OFFER_MAX_TTL` | `3600000` | Maximum offer TTL in ms (1 hour) |
309
- | `MAX_OFFERS_PER_REQUEST` | `10` | Maximum offers per create request |
310
-
311
- ## Database Schema
312
-
313
- ### usernames
314
- - `username` (PK): Claimed username
315
- - `public_key`: Ed25519 public key (base64)
316
- - `claimed_at`: Claim timestamp
317
- - `expires_at`: Expiry timestamp (365 days)
318
- - `last_used`: Last activity timestamp
319
- - `metadata`: Optional JSON metadata
320
-
321
- ### services
322
- - `id` (PK): Service ID (UUID)
323
- - `username` (FK): Owner username
324
- - `service_fqn`: Fully qualified name (com.example.chat@1.0.0)
325
- - `is_public`: Public/private flag
326
- - `metadata`: JSON metadata
327
- - `created_at`, `expires_at`: Timestamps
328
-
329
- ### offers
330
- - `id` (PK): Offer ID (hash of SDP)
331
- - `peer_id` (FK): Owner peer ID
332
- - `service_id` (FK): Optional link to service (null for standalone offers)
333
- - `sdp`: WebRTC offer SDP
334
- - `answerer_peer_id`: Peer ID of answerer (null until answered)
335
- - `answer_sdp`: WebRTC answer SDP (null until answered)
336
- - `created_at`, `expires_at`, `last_seen`: Timestamps
337
-
338
- ### service_index (privacy layer)
339
- - `uuid` (PK): Random UUID for discovery
340
- - `service_id` (FK): Links to service
341
- - `username`, `service_fqn`: Denormalized for performance
342
-
343
- ## Security
344
235
 
345
- ### Username Claiming
346
- - **Algorithm**: Ed25519 signatures
347
- - **Message Format**: `claim:{username}:{timestamp}`
348
- - **Replay Protection**: Timestamp must be within 5 minutes
349
- - **Key Management**: Private keys never leave the client
236
+ 📚 See [ADVANCED.md](./ADVANCED.md#configuration) for complete configuration reference.
350
237
 
351
- ### Service Publishing
352
- - **Ownership Verification**: Every publish requires username signature
353
- - **Message Format**: `publish:{username}:{serviceFqn}:{timestamp}`
354
- - **Auto-Renewal**: Publishing a service extends username expiry
238
+ ## Documentation
355
239
 
356
- ### Privacy
357
- - **Private Services**: Only UUID exposed, FQN hidden
358
- - **Public Services**: FQN and metadata visible
359
- - **No Enumeration**: Cannot list all services without knowing FQN
240
+ 📚 **[ADVANCED.md](./ADVANCED.md)** - Comprehensive guide including:
241
+ - Complete RPC method reference with examples
242
+ - Full configuration options
243
+ - Database schema documentation
244
+ - Security implementation details
245
+ - Migration guides
360
246
 
361
- ## Migration from V1
247
+ ## Security
362
248
 
363
- V2 is a **breaking change** that removes topic-based discovery. See [MIGRATION.md](../MIGRATION.md) for detailed migration guide.
249
+ All authenticated operations require Ed25519 signatures:
250
+ - **Message Format**: `{method}:{username}:{context}:{timestamp}`
251
+ - **Signature**: Base64-encoded Ed25519 signature of the message
252
+ - **Replay Protection**: Timestamps must be within 5 minutes
253
+ - **Username Ownership**: Verified via public key signature
364
254
 
365
- **Key Changes:**
366
- - ❌ Removed: Topic-based discovery, bloom filters, public peer listings
367
- - ✅ Added: Username claiming, service publishing, UUID-based privacy
255
+ See [ADVANCED.md](./ADVANCED.md#security) for detailed security documentation.
368
256
 
369
257
  ## License
370
258