@xtr-dev/rondevu-server 0.4.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,351 +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
98
-
99
- **Request:**
100
- ```json
101
- {
102
- "publicKey": "base64-encoded-ed25519-public-key",
103
- "signature": "base64-encoded-signature",
104
- "message": "claim:alice:1733404800000"
105
- }
106
- ```
100
+ ### Response Format
107
101
 
108
- **Response:**
102
+ **Single response:**
109
103
  ```json
110
104
  {
111
- "username": "alice",
112
- "claimedAt": 1733404800000,
113
- "expiresAt": 1765027200000
105
+ "success": true,
106
+ "result": { /* method-specific data */ }
114
107
  }
115
108
  ```
116
109
 
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
122
-
123
- #### `GET /users/:username/services/:fqn`
124
- Get service by username and FQN with semver-compatible matching
125
-
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
131
-
132
- **Response:**
110
+ **Error response:**
133
111
  ```json
134
112
  {
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
113
+ "success": false,
114
+ "error": "Error message"
145
115
  }
146
116
  ```
147
117
 
148
- **Note:** Returns a single available offer from the service. If all offers are in use, returns 503.
118
+ **Batch responses:** Array of responses matching request array order.
149
119
 
150
- ### Service Management (RESTful)
120
+ ## Core Methods
151
121
 
152
- #### `POST /users/:username/services`
153
- Publish a service with multiple offers (requires authentication and username signature)
122
+ ### Username Management
154
123
 
155
- **Headers:**
156
- - `Authorization: Bearer {peerId}:{secret}`
157
-
158
- **Request:**
159
- ```json
124
+ ```typescript
125
+ // Check username availability
126
+ POST /rpc
160
127
  {
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"
128
+ "method": "getUser",
129
+ "params": { "username": "alice" }
171
130
  }
172
- ```
173
131
 
174
- **Response (Full service details):**
175
- ```json
132
+ // Claim username (requires signature)
133
+ POST /rpc
176
134
  {
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
135
+ "method": "claimUsername",
136
+ "message": "claim:alice:1733404800000",
137
+ "signature": "base64-signature",
138
+ "params": {
139
+ "username": "alice",
140
+ "publicKey": "base64-public-key"
141
+ }
199
142
  }
200
143
  ```
201
144
 
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
214
-
215
- **Response:**
216
- ```json
217
- {
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
227
- }
228
- ```
229
-
230
- #### `DELETE /users/:username/services/:fqn`
231
- Unpublish a service (requires authentication and ownership)
232
-
233
- **Headers:**
234
- - `Authorization: Bearer {peerId}:{secret}`
145
+ ### Service Publishing
235
146
 
236
- **Request:**
237
- ```json
147
+ ```typescript
148
+ // Publish service (requires signature)
149
+ POST /rpc
238
150
  {
239
- "username": "alice"
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
+ }
240
159
  }
241
160
  ```
242
161
 
243
- ### WebRTC Signaling (Service-Based)
244
-
245
- #### `POST /services/:uuid/answer`
246
- Answer a service offer (requires authentication)
162
+ ### Service Discovery
247
163
 
248
- **Headers:**
249
- - `Authorization: Bearer {peerId}:{secret}`
250
-
251
- **Request:**
252
- ```json
164
+ ```typescript
165
+ // Get specific service
166
+ POST /rpc
253
167
  {
254
- "sdp": "v=0..."
168
+ "method": "getService",
169
+ "params": { "serviceFqn": "chat:1.0.0@alice" }
255
170
  }
256
- ```
257
171
 
258
- **Response:**
259
- ```json
172
+ // Random discovery
173
+ POST /rpc
260
174
  {
261
- "success": true,
262
- "offerId": "offer-hash"
175
+ "method": "getService",
176
+ "params": { "serviceFqn": "chat:1.0.0" }
263
177
  }
264
- ```
265
178
 
266
- #### `GET /services/:uuid/answer`
267
- Get answer for a service (offerer polls this)
268
-
269
- **Headers:**
270
- - `Authorization: Bearer {peerId}:{secret}`
271
-
272
- **Response:**
273
- ```json
179
+ // Paginated discovery
180
+ POST /rpc
274
181
  {
275
- "offerId": "offer-hash",
276
- "answererId": "answerer-peer-id",
277
- "sdp": "v=0...",
278
- "answeredAt": 1733404800000
182
+ "method": "getService",
183
+ "params": {
184
+ "serviceFqn": "chat:1.0.0",
185
+ "limit": 10,
186
+ "offset": 0
187
+ }
279
188
  }
280
189
  ```
281
190
 
282
- **Note:** Returns 404 if not yet answered
283
-
284
- #### `POST /services/:uuid/ice-candidates`
285
- Post ICE candidates for a service (requires authentication)
191
+ ### WebRTC Signaling
286
192
 
287
- **Headers:**
288
- - `Authorization: Bearer {peerId}:{secret}`
289
-
290
- **Request:**
291
- ```json
193
+ ```typescript
194
+ // Answer offer (requires signature)
195
+ POST /rpc
292
196
  {
293
- "candidates": ["candidate:1 1 UDP..."],
294
- "offerId": "optional-offer-id"
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
+ }
295
205
  }
296
- ```
297
206
 
298
- **Response:**
299
- ```json
207
+ // Add ICE candidates (requires signature)
208
+ POST /rpc
300
209
  {
301
- "count": 1,
302
- "offerId": "offer-hash"
210
+ "method": "addIceCandidates",
211
+ "params": {
212
+ "serviceFqn": "chat:1.0.0@alice",
213
+ "offerId": "offer-id",
214
+ "candidates": [{ /* RTCIceCandidateInit */ }]
215
+ }
303
216
  }
304
- ```
305
-
306
- **Note:** If `offerId` is omitted, the server will auto-detect the peer's offer
307
-
308
- #### `GET /services/:uuid/ice-candidates?since=1234567890&offerId=optional-offer-id`
309
- Get ICE candidates from the other peer (requires authentication)
310
-
311
- **Headers:**
312
- - `Authorization: Bearer {peerId}:{secret}`
313
217
 
314
- **Response:**
315
- ```json
218
+ // Poll for answers and ICE candidates (requires signature)
219
+ POST /rpc
316
220
  {
317
- "candidates": [
318
- {
319
- "candidate": "candidate:1 1 UDP...",
320
- "createdAt": 1733404800000
321
- }
322
- ],
323
- "offerId": "offer-hash"
221
+ "method": "poll",
222
+ "params": { "since": 1733404800000 }
324
223
  }
325
224
  ```
326
225
 
327
- **Note:** Returns candidates from the opposite role (offerer gets answerer candidates and vice versa)
328
-
329
226
  ## Configuration
330
227
 
331
- Environment variables:
228
+ Quick reference for common environment variables:
332
229
 
333
230
  | Variable | Default | Description |
334
231
  |----------|---------|-------------|
335
232
  | `PORT` | `3000` | Server port (Node.js/Docker) |
336
233
  | `CORS_ORIGINS` | `*` | Comma-separated allowed origins |
337
234
  | `STORAGE_PATH` | `./rondevu.db` | SQLite database path (use `:memory:` for in-memory) |
338
- | `VERSION` | `2.0.0` | Server version (semver) |
339
- | `AUTH_SECRET` | Random 32-byte hex | Secret key for credential encryption (required for production) |
340
- | `OFFER_DEFAULT_TTL` | `300000` | Default offer TTL in ms (5 minutes) |
341
- | `OFFER_MIN_TTL` | `60000` | Minimum offer TTL in ms (1 minute) |
342
- | `OFFER_MAX_TTL` | `3600000` | Maximum offer TTL in ms (1 hour) |
343
- | `MAX_OFFERS_PER_REQUEST` | `10` | Maximum offers per create request |
344
-
345
- ## Database Schema
346
-
347
- ### usernames
348
- - `username` (PK): Claimed username
349
- - `public_key`: Ed25519 public key (base64)
350
- - `claimed_at`: Claim timestamp
351
- - `expires_at`: Expiry timestamp (365 days)
352
- - `last_used`: Last activity timestamp
353
- - `metadata`: Optional JSON metadata
354
-
355
- ### services
356
- - `id` (PK): Service ID (UUID)
357
- - `username` (FK): Owner username
358
- - `service_fqn`: Fully qualified name (com.example.chat@1.0.0)
359
- - `is_public`: Public/private flag
360
- - `metadata`: JSON metadata
361
- - `created_at`, `expires_at`: Timestamps
362
-
363
- ### offers
364
- - `id` (PK): Offer ID (hash of SDP)
365
- - `peer_id` (FK): Owner peer ID
366
- - `service_id` (FK): Optional link to service (null for standalone offers)
367
- - `sdp`: WebRTC offer SDP
368
- - `answerer_peer_id`: Peer ID of answerer (null until answered)
369
- - `answer_sdp`: WebRTC answer SDP (null until answered)
370
- - `created_at`, `expires_at`, `last_seen`: Timestamps
371
-
372
- ### service_index (privacy layer)
373
- - `uuid` (PK): Random UUID for discovery
374
- - `service_id` (FK): Links to service
375
- - `username`, `service_fqn`: Denormalized for performance
376
235
 
377
- ## Security
236
+ 📚 See [ADVANCED.md](./ADVANCED.md#configuration) for complete configuration reference.
378
237
 
379
- ### Username Claiming
380
- - **Algorithm**: Ed25519 signatures
381
- - **Message Format**: `claim:{username}:{timestamp}`
382
- - **Replay Protection**: Timestamp must be within 5 minutes
383
- - **Key Management**: Private keys never leave the client
238
+ ## Documentation
384
239
 
385
- ### Service Publishing
386
- - **Ownership Verification**: Every publish requires username signature
387
- - **Message Format**: `publish:{username}:{serviceFqn}:{timestamp}`
388
- - **Auto-Renewal**: Publishing a service extends username expiry
389
-
390
- ### Privacy
391
- - **Private Services**: Only UUID exposed, FQN hidden
392
- - **Public Services**: FQN and metadata visible
393
- - **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
394
246
 
395
- ## Migration from V1
247
+ ## Security
396
248
 
397
- 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
398
254
 
399
- **Key Changes:**
400
- - ❌ Removed: Topic-based discovery, bloom filters, public peer listings
401
- - ✅ Added: Username claiming, service publishing, UUID-based privacy
255
+ See [ADVANCED.md](./ADVANCED.md#security) for detailed security documentation.
402
256
 
403
257
  ## License
404
258