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:
2025-11-16 22:03:49 +01:00
parent e052464482
commit 3530213870
4 changed files with 62 additions and 11 deletions

View File

@@ -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)
- **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
- **Multi-Offer Management**: Create and manage multiple offers per peer
- **Complete WebRTC Signaling**: Full offer/answer and ICE candidate exchange
@@ -68,7 +69,8 @@ peer.on('datachannel', (channel) => {
// Create offer and advertise on topics
const offerId = await peer.createOffer({
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);
@@ -121,11 +123,50 @@ if (offers.length > 0) {
// Answer the offer
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
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([{
sdp: 'v=0...', // Your WebRTC offer SDP
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
@@ -433,7 +475,8 @@ const offers = await client.offers.create([
{
sdp: 'v=0...',
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);
```
#### `client.offers.answer(offerId, sdp)`
#### `client.offers.answer(offerId, sdp, secret?)`
Answer an offer (locks it to answerer).
```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()`
Poll for answers to your offers.

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@xtr-dev/rondevu-client",
"version": "0.7.3",
"version": "0.7.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@xtr-dev/rondevu-client",
"version": "0.7.3",
"version": "0.7.6",
"license": "MIT",
"dependencies": {
"@xtr-dev/rondevu-client": "^0.5.1"

View File

@@ -1,6 +1,6 @@
{
"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",
"type": "module",
"main": "dist/index.js",

View File

@@ -8,6 +8,7 @@ export interface CreateOfferRequest {
sdp: string;
topics: string[];
ttl?: number;
secret?: string;
}
export interface Offer {
@@ -18,6 +19,8 @@ export interface Offer {
createdAt?: number;
expiresAt: number;
lastSeen: number;
secret?: string;
hasSecret?: boolean;
answererPeerId?: string;
answerSdp?: string;
answeredAt?: number;
@@ -221,14 +224,14 @@ export class RondevuOffers {
/**
* 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`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: RondevuAuth.createAuthHeader(this.credentials),
},
body: JSON.stringify({ sdp }),
body: JSON.stringify({ sdp, secret }),
});
if (!response.ok) {