mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-10 02:43:25 +00:00
Update README with secret field documentation
- Document secret parameter in offer creation examples - Add Protected Offers section with detailed usage - Update API reference for create() and answer() methods - Show hasSecret flag in discovery responses 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
60
README.md
60
README.md
@@ -16,6 +16,7 @@ TypeScript/JavaScript client for Rondevu, providing topic-based peer discovery,
|
|||||||
|
|
||||||
- **Topic-Based Discovery**: Find peers by topics (e.g., torrent infohashes)
|
- **Topic-Based Discovery**: Find peers by topics (e.g., torrent infohashes)
|
||||||
- **Stateless Authentication**: No server-side sessions, portable credentials
|
- **Stateless Authentication**: No server-side sessions, portable credentials
|
||||||
|
- **Protected Connections**: Optional secret-protected offers for access control
|
||||||
- **Bloom Filters**: Efficient peer exclusion for repeated discoveries
|
- **Bloom Filters**: Efficient peer exclusion for repeated discoveries
|
||||||
- **Multi-Offer Management**: Create and manage multiple offers per peer
|
- **Multi-Offer Management**: Create and manage multiple offers per peer
|
||||||
- **Complete WebRTC Signaling**: Full offer/answer and ICE candidate exchange
|
- **Complete WebRTC Signaling**: Full offer/answer and ICE candidate exchange
|
||||||
@@ -68,7 +69,8 @@ peer.on('datachannel', (channel) => {
|
|||||||
// Create offer and advertise on topics
|
// Create offer and advertise on topics
|
||||||
const offerId = await peer.createOffer({
|
const offerId = await peer.createOffer({
|
||||||
topics: ['my-app', 'room-123'],
|
topics: ['my-app', 'room-123'],
|
||||||
ttl: 300000 // 5 minutes
|
ttl: 300000, // 5 minutes
|
||||||
|
secret: 'my-secret-password' // Optional: protect offer (max 128 chars)
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Offer created:', offerId);
|
console.log('Offer created:', offerId);
|
||||||
@@ -121,11 +123,50 @@ if (offers.length > 0) {
|
|||||||
|
|
||||||
// Answer the offer
|
// Answer the offer
|
||||||
await peer.answer(offer.id, offer.sdp, {
|
await peer.answer(offer.id, offer.sdp, {
|
||||||
topics: offer.topics
|
topics: offer.topics,
|
||||||
|
secret: 'my-secret-password' // Required if offer.hasSecret is true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Protected Offers
|
||||||
|
|
||||||
|
You can protect offers with a secret to control who can answer them. This is useful for private rooms or invite-only connections.
|
||||||
|
|
||||||
|
### Creating a Protected Offer
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const offerId = await peer.createOffer({
|
||||||
|
topics: ['private-room'],
|
||||||
|
secret: 'my-secret-password' // Max 128 characters
|
||||||
|
});
|
||||||
|
|
||||||
|
// Share the secret with authorized peers through a secure channel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Answering a Protected Offer
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const offers = await client.offers.findByTopic('private-room');
|
||||||
|
|
||||||
|
// Check if offer requires a secret
|
||||||
|
if (offers[0].hasSecret) {
|
||||||
|
console.log('This offer requires a secret');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide the secret when answering
|
||||||
|
await peer.answer(offers[0].id, offers[0].sdp, {
|
||||||
|
topics: offers[0].topics,
|
||||||
|
secret: 'my-secret-password' // Must match the offer's secret
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- The actual secret is never exposed in public API responses - only a `hasSecret` boolean flag
|
||||||
|
- Answerers must provide the correct secret, or the answer will be rejected
|
||||||
|
- Secrets are limited to 128 characters
|
||||||
|
- Use this for access control, not for cryptographic security (use end-to-end encryption for that)
|
||||||
|
|
||||||
## Connection Lifecycle
|
## Connection Lifecycle
|
||||||
|
|
||||||
The `RondevuPeer` uses a state machine for connection management:
|
The `RondevuPeer` uses a state machine for connection management:
|
||||||
@@ -368,7 +409,8 @@ localStorage.setItem('rondevu-creds', JSON.stringify(creds));
|
|||||||
const offers = await client.offers.create([{
|
const offers = await client.offers.create([{
|
||||||
sdp: 'v=0...', // Your WebRTC offer SDP
|
sdp: 'v=0...', // Your WebRTC offer SDP
|
||||||
topics: ['movie-xyz', 'hd-content'],
|
topics: ['movie-xyz', 'hd-content'],
|
||||||
ttl: 300000 // 5 minutes
|
ttl: 300000, // 5 minutes
|
||||||
|
secret: 'my-secret-password' // Optional: protect offer (max 128 chars)
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
// Discover peers by topic
|
// Discover peers by topic
|
||||||
@@ -433,7 +475,8 @@ const offers = await client.offers.create([
|
|||||||
{
|
{
|
||||||
sdp: 'v=0...',
|
sdp: 'v=0...',
|
||||||
topics: ['topic-1', 'topic-2'],
|
topics: ['topic-1', 'topic-2'],
|
||||||
ttl: 300000 // optional, default 5 minutes
|
ttl: 300000, // optional, default 5 minutes
|
||||||
|
secret: 'my-secret-password' // optional, max 128 chars
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
@@ -462,13 +505,18 @@ Delete a specific offer.
|
|||||||
await client.offers.delete(offerId);
|
await client.offers.delete(offerId);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `client.offers.answer(offerId, sdp)`
|
#### `client.offers.answer(offerId, sdp, secret?)`
|
||||||
Answer an offer (locks it to answerer).
|
Answer an offer (locks it to answerer).
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
await client.offers.answer(offerId, answerSdp);
|
await client.offers.answer(offerId, answerSdp, 'my-secret-password');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `offerId`: The offer ID to answer
|
||||||
|
- `sdp`: The WebRTC answer SDP
|
||||||
|
- `secret` (optional): Required if the offer has `hasSecret: true`
|
||||||
|
|
||||||
#### `client.offers.getAnswers()`
|
#### `client.offers.getAnswers()`
|
||||||
Poll for answers to your offers.
|
Poll for answers to your offers.
|
||||||
|
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@xtr-dev/rondevu-client",
|
"name": "@xtr-dev/rondevu-client",
|
||||||
"version": "0.7.3",
|
"version": "0.7.6",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@xtr-dev/rondevu-client",
|
"name": "@xtr-dev/rondevu-client",
|
||||||
"version": "0.7.3",
|
"version": "0.7.6",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xtr-dev/rondevu-client": "^0.5.1"
|
"@xtr-dev/rondevu-client": "^0.5.1"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@xtr-dev/rondevu-client",
|
"name": "@xtr-dev/rondevu-client",
|
||||||
"version": "0.7.5",
|
"version": "0.7.6",
|
||||||
"description": "TypeScript client for Rondevu topic-based peer discovery and signaling server",
|
"description": "TypeScript client for Rondevu topic-based peer discovery and signaling server",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface CreateOfferRequest {
|
|||||||
sdp: string;
|
sdp: string;
|
||||||
topics: string[];
|
topics: string[];
|
||||||
ttl?: number;
|
ttl?: number;
|
||||||
|
secret?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Offer {
|
export interface Offer {
|
||||||
@@ -18,6 +19,8 @@ export interface Offer {
|
|||||||
createdAt?: number;
|
createdAt?: number;
|
||||||
expiresAt: number;
|
expiresAt: number;
|
||||||
lastSeen: number;
|
lastSeen: number;
|
||||||
|
secret?: string;
|
||||||
|
hasSecret?: boolean;
|
||||||
answererPeerId?: string;
|
answererPeerId?: string;
|
||||||
answerSdp?: string;
|
answerSdp?: string;
|
||||||
answeredAt?: number;
|
answeredAt?: number;
|
||||||
@@ -221,14 +224,14 @@ export class RondevuOffers {
|
|||||||
/**
|
/**
|
||||||
* Answer an offer
|
* Answer an offer
|
||||||
*/
|
*/
|
||||||
async answer(offerId: string, sdp: string): Promise<void> {
|
async answer(offerId: string, sdp: string, secret?: string): Promise<void> {
|
||||||
const response = await this.fetchFn(`${this.baseUrl}/offers/${encodeURIComponent(offerId)}/answer`, {
|
const response = await this.fetchFn(`${this.baseUrl}/offers/${encodeURIComponent(offerId)}/answer`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Authorization: RondevuAuth.createAuthHeader(this.credentials),
|
Authorization: RondevuAuth.createAuthHeader(this.credentials),
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ sdp }),
|
body: JSON.stringify({ sdp, secret }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
Reference in New Issue
Block a user