@xtr-dev/rondevu-client 0.21.5 → 0.21.8

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.
@@ -11,7 +11,7 @@ export class OffererConnection extends RondevuConnection {
11
11
  constructor(options) {
12
12
  // Force reconnectEnabled: false for offerer connections (offers are ephemeral)
13
13
  super(undefined, { ...options.config, reconnectEnabled: false }, options.webrtcAdapter);
14
- this._peerUsername = null;
14
+ this._peerPublicKey = null;
15
15
  // Rotation tracking
16
16
  this.rotationLock = new AsyncLock();
17
17
  this.rotating = false;
@@ -19,7 +19,7 @@ export class OffererConnection extends RondevuConnection {
19
19
  // ICE candidate buffering (for candidates received before answer is processed)
20
20
  this.pendingIceCandidates = [];
21
21
  this.api = options.api;
22
- this.ownerUsername = options.ownerUsername;
22
+ this.ownerPublicKey = options.ownerPublicKey;
23
23
  this.offerId = options.offerId;
24
24
  // Use the already-created peer connection and data channel
25
25
  this.pc = options.pc;
@@ -49,7 +49,7 @@ export class OffererConnection extends RondevuConnection {
49
49
  /**
50
50
  * Process an answer from the answerer
51
51
  */
52
- async processAnswer(sdp, answererId) {
52
+ async processAnswer(sdp, answererPublicKey) {
53
53
  if (!this.pc) {
54
54
  this.debug('Cannot process answer: peer connection not initialized');
55
55
  return;
@@ -82,10 +82,10 @@ export class OffererConnection extends RondevuConnection {
82
82
  type: 'answer',
83
83
  sdp,
84
84
  });
85
- // Store the peer username
86
- this._peerUsername = answererId;
87
- this.debug(`Answer processed successfully from ${answererId}`);
88
- this.emit('answer:processed', this.offerId, answererId);
85
+ // Store the peer public key
86
+ this._peerPublicKey = answererPublicKey;
87
+ this.debug(`Answer processed successfully from ${answererPublicKey}`);
88
+ this.emit('answer:processed', this.offerId, answererPublicKey);
89
89
  // Apply any buffered ICE candidates that arrived before the answer
90
90
  if (this.pendingIceCandidates.length > 0) {
91
91
  this.debug(`Applying ${this.pendingIceCandidates.length} buffered ICE candidates`);
@@ -129,10 +129,10 @@ export class OffererConnection extends RondevuConnection {
129
129
  this.offerId = newOfferId;
130
130
  this.pc = newPc;
131
131
  this.dc = newDc || null;
132
- // 3. Reset answer processing flags, peer username, and pending candidates
132
+ // 3. Reset answer processing flags, peer public key, and pending candidates
133
133
  this.answerProcessed = false;
134
134
  this.answerSdpFingerprint = null;
135
- this._peerUsername = null;
135
+ this._peerPublicKey = null;
136
136
  this.pendingIceCandidates = [];
137
137
  // 4. Setup event handlers for new peer connection
138
138
  this.pc.onicecandidate = event => this.handleIceCandidate(event);
@@ -217,10 +217,10 @@ export class OffererConnection extends RondevuConnection {
217
217
  return this.api;
218
218
  }
219
219
  /**
220
- * Get the owner username
220
+ * Get the owner public key
221
221
  */
222
- getOwnerUsername() {
223
- return this.ownerUsername;
222
+ getOwnerPublicKey() {
223
+ return this.ownerPublicKey;
224
224
  }
225
225
  /**
226
226
  * Offerers accept all ICE candidates (no filtering)
@@ -256,11 +256,11 @@ export class OffererConnection extends RondevuConnection {
256
256
  return this.offerId;
257
257
  }
258
258
  /**
259
- * Get the peer username (who answered this offer)
259
+ * Get the peer public key (who answered this offer)
260
260
  * Returns null if no answer has been processed yet
261
261
  */
262
- get peerUsername() {
263
- return this._peerUsername;
262
+ get peerPublicKey() {
263
+ return this._peerPublicKey;
264
264
  }
265
265
  /**
266
266
  * Handle remote ICE candidates received from polling
@@ -20,3 +20,4 @@ export { ICE_SERVER_PRESETS } from './ice-config.js';
20
20
  export type { RondevuOptions, OfferOptions, OfferHandle, DiscoverOptions, DiscoverResult, } from './rondevu.js';
21
21
  export type { PeerState, PeerOptions } from './peer.js';
22
22
  export type { IceServerPreset } from './ice-config.js';
23
+ export type { KeyPair, CryptoAdapter } from '../crypto/adapter.js';
@@ -11,7 +11,7 @@ export type OfferFactory = (pc: RTCPeerConnection) => Promise<{
11
11
  export interface OfferPoolOptions {
12
12
  api: RondevuAPI;
13
13
  tags: string[];
14
- ownerUsername: string;
14
+ ownerPublicKey: string;
15
15
  maxOffers: number;
16
16
  offerFactory: OfferFactory;
17
17
  ttl: number;
@@ -20,6 +20,12 @@ export interface OfferPoolOptions {
20
20
  webrtcAdapter: WebRTCAdapter;
21
21
  connectionConfig?: Partial<ConnectionConfig>;
22
22
  debugEnabled?: boolean;
23
+ /**
24
+ * Delay in milliseconds between creating each offer during pool filling.
25
+ * Helps avoid rate limiting when creating multiple offers.
26
+ * Default: 100ms
27
+ */
28
+ offerCreationThrottleMs?: number;
23
29
  }
24
30
  interface OfferPoolEvents {
25
31
  'connection:opened': (offerId: string, connection: OffererConnection, matchedTags?: string[]) => void;
@@ -35,7 +41,7 @@ interface OfferPoolEvents {
35
41
  export declare class OfferPool extends EventEmitter<OfferPoolEvents> {
36
42
  private readonly api;
37
43
  private tags;
38
- private readonly ownerUsername;
44
+ private readonly ownerPublicKey;
39
45
  private readonly maxOffers;
40
46
  private readonly offerFactory;
41
47
  private readonly ttl;
@@ -44,6 +50,7 @@ export declare class OfferPool extends EventEmitter<OfferPoolEvents> {
44
50
  private readonly webrtcAdapter;
45
51
  private readonly connectionConfig?;
46
52
  private readonly debugEnabled;
53
+ private readonly offerCreationThrottleMs;
47
54
  private readonly activeConnections;
48
55
  private readonly matchedTagsByOffer;
49
56
  private readonly fillLock;
@@ -16,7 +16,7 @@ export class OfferPool extends EventEmitter {
16
16
  this.running = false;
17
17
  this.api = options.api;
18
18
  this.tags = options.tags;
19
- this.ownerUsername = options.ownerUsername;
19
+ this.ownerPublicKey = options.ownerPublicKey;
20
20
  this.webrtcAdapter = options.webrtcAdapter;
21
21
  this.maxOffers = options.maxOffers;
22
22
  this.offerFactory = options.offerFactory;
@@ -25,6 +25,7 @@ export class OfferPool extends EventEmitter {
25
25
  this.iceTransportPolicy = options.iceTransportPolicy;
26
26
  this.connectionConfig = options.connectionConfig;
27
27
  this.debugEnabled = options.debugEnabled || false;
28
+ this.offerCreationThrottleMs = options.offerCreationThrottleMs ?? 100;
28
29
  }
29
30
  /**
30
31
  * Start filling offers
@@ -116,6 +117,10 @@ export class OfferPool extends EventEmitter {
116
117
  for (let i = 0; i < needed; i++) {
117
118
  try {
118
119
  await this.createOffer();
120
+ // Throttle between offer creations to avoid rate limiting
121
+ if (i < needed - 1 && this.offerCreationThrottleMs > 0) {
122
+ await new Promise(resolve => setTimeout(resolve, this.offerCreationThrottleMs));
123
+ }
119
124
  }
120
125
  catch (err) {
121
126
  console.error('[OfferPool] Failed to create offer:', err);
@@ -195,7 +200,7 @@ export class OfferPool extends EventEmitter {
195
200
  // Create OffererConnection instance
196
201
  const connection = new OffererConnection({
197
202
  api: this.api,
198
- ownerUsername: this.ownerUsername,
203
+ ownerPublicKey: this.ownerPublicKey,
199
204
  offerId,
200
205
  pc,
201
206
  dc,
@@ -271,7 +276,7 @@ export class OfferPool extends EventEmitter {
271
276
  this.matchedTagsByOffer.set(data.offerId, data.matchedTags);
272
277
  }
273
278
  try {
274
- await connection.processAnswer(data.sdp, data.answererId);
279
+ await connection.processAnswer(data.sdp, data.answererPublicKey);
275
280
  // Create replacement offer
276
281
  this.fillOffers();
277
282
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Peer - Clean DX wrapper for peer-to-peer connections
3
3
  *
4
- * Provides a simple interface for connecting to a peer by tags/username,
4
+ * Provides a simple interface for connecting to a peer by tags/publicKey,
5
5
  * with automatic reconnection and message buffering.
6
6
  */
7
7
  import { EventEmitter } from 'eventemitter3';
@@ -37,8 +37,8 @@ export type PeerEventName = keyof PeerEventMap;
37
37
  export interface PeerOptions {
38
38
  /** Tags to match for peer discovery */
39
39
  tags: string[];
40
- /** Optional: connect to specific username */
41
- username?: string;
40
+ /** Optional: connect to specific public key */
41
+ publicKey?: string;
42
42
  /** Optional: custom RTC configuration */
43
43
  rtcConfig?: RTCConfiguration;
44
44
  /** Optional: connection behavior configuration */
@@ -60,12 +60,12 @@ export interface PeerInternalOptions extends PeerOptions {
60
60
  * @example
61
61
  * ```typescript
62
62
  * const peer = await rondevu.peer({
63
- * username: 'alice',
63
+ * publicKey: 'abc123...',
64
64
  * tags: ['chat']
65
65
  * })
66
66
  *
67
67
  * peer.on('open', () => {
68
- * console.log('Connected to', peer.peerUsername)
68
+ * console.log('Connected to', peer.peerPublicKey)
69
69
  * peer.send('Hello!')
70
70
  * })
71
71
  *
@@ -86,14 +86,14 @@ export declare class Peer extends EventEmitter<PeerEventMap> {
86
86
  private connection;
87
87
  private api;
88
88
  private tags;
89
- private targetUsername?;
89
+ private targetPublicKey?;
90
90
  private iceServers;
91
91
  private iceTransportPolicy?;
92
92
  private webrtcAdapter?;
93
93
  private connectionConfig?;
94
94
  private debugEnabled;
95
95
  private _state;
96
- private _peerUsername;
96
+ private _peerPublicKey;
97
97
  private _offerId;
98
98
  constructor(options: PeerInternalOptions);
99
99
  /**
@@ -113,9 +113,9 @@ export declare class Peer extends EventEmitter<PeerEventMap> {
113
113
  */
114
114
  get state(): PeerState;
115
115
  /**
116
- * Username of the connected peer
116
+ * Public key of the connected peer
117
117
  */
118
- get peerUsername(): string;
118
+ get peerPublicKey(): string;
119
119
  /**
120
120
  * The offer ID being used for this connection
121
121
  */
package/dist/core/peer.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Peer - Clean DX wrapper for peer-to-peer connections
3
3
  *
4
- * Provides a simple interface for connecting to a peer by tags/username,
4
+ * Provides a simple interface for connecting to a peer by tags/publicKey,
5
5
  * with automatic reconnection and message buffering.
6
6
  */
7
7
  import { EventEmitter } from 'eventemitter3';
@@ -13,12 +13,12 @@ import { ConnectionState } from '../connections/events.js';
13
13
  * @example
14
14
  * ```typescript
15
15
  * const peer = await rondevu.peer({
16
- * username: 'alice',
16
+ * publicKey: 'abc123...',
17
17
  * tags: ['chat']
18
18
  * })
19
19
  *
20
20
  * peer.on('open', () => {
21
- * console.log('Connected to', peer.peerUsername)
21
+ * console.log('Connected to', peer.peerPublicKey)
22
22
  * peer.send('Hello!')
23
23
  * })
24
24
  *
@@ -40,11 +40,11 @@ export class Peer extends EventEmitter {
40
40
  super();
41
41
  this.connection = null;
42
42
  this._state = 'connecting';
43
- this._peerUsername = '';
43
+ this._peerPublicKey = '';
44
44
  this._offerId = '';
45
45
  this.api = options.api;
46
46
  this.tags = options.tags;
47
- this.targetUsername = options.username;
47
+ this.targetPublicKey = options.publicKey;
48
48
  this.iceServers = options.iceServers;
49
49
  this.iceTransportPolicy = options.iceTransportPolicy;
50
50
  this.webrtcAdapter = options.webrtcAdapter;
@@ -56,7 +56,7 @@ export class Peer extends EventEmitter {
56
56
  */
57
57
  async initialize() {
58
58
  this.debug('Initializing peer connection');
59
- this.debug(`Tags: ${this.tags.join(', ')}${this.targetUsername ? `, username: ${this.targetUsername}` : ''}`);
59
+ this.debug(`Tags: ${this.tags.join(', ')}${this.targetPublicKey ? `, publicKey: ${this.targetPublicKey}` : ''}`);
60
60
  // Discover offers
61
61
  const result = (await this.api.discover({
62
62
  tags: this.tags,
@@ -65,23 +65,23 @@ export class Peer extends EventEmitter {
65
65
  if (!result.offers || result.offers.length === 0) {
66
66
  throw new Error(`No peers found for tags: ${this.tags.join(', ')}`);
67
67
  }
68
- // Filter by username if specified
68
+ // Filter by publicKey if specified
69
69
  let availableOffers = result.offers;
70
- if (this.targetUsername) {
71
- availableOffers = result.offers.filter((o) => o.username === this.targetUsername);
70
+ if (this.targetPublicKey) {
71
+ availableOffers = result.offers.filter((o) => o.publicKey === this.targetPublicKey);
72
72
  if (availableOffers.length === 0) {
73
- throw new Error(`No peers found for tags: ${this.tags.join(', ')} from @${this.targetUsername}`);
73
+ throw new Error(`No peers found for tags: ${this.tags.join(', ')} from ${this.targetPublicKey}`);
74
74
  }
75
75
  }
76
76
  // Pick a random offer
77
77
  const offer = availableOffers[Math.floor(Math.random() * availableOffers.length)];
78
- this._peerUsername = offer.username;
78
+ this._peerPublicKey = offer.publicKey;
79
79
  this._offerId = offer.offerId;
80
- this.debug(`Selected offer ${offer.offerId} from @${offer.username}`);
80
+ this.debug(`Selected offer ${offer.offerId} from ${offer.publicKey}`);
81
81
  // Create the underlying AnswererConnection
82
82
  this.connection = new AnswererConnection({
83
83
  api: this.api,
84
- ownerUsername: offer.username,
84
+ ownerPublicKey: offer.publicKey,
85
85
  tags: offer.tags,
86
86
  offerId: offer.offerId,
87
87
  offerSdp: offer.sdp,
@@ -180,10 +180,10 @@ export class Peer extends EventEmitter {
180
180
  return this._state;
181
181
  }
182
182
  /**
183
- * Username of the connected peer
183
+ * Public key of the connected peer
184
184
  */
185
- get peerUsername() {
186
- return this._peerUsername;
185
+ get peerPublicKey() {
186
+ return this._peerPublicKey;
187
187
  }
188
188
  /**
189
189
  * The offer ID being used for this connection
@@ -11,7 +11,7 @@ import { EventEmitter } from 'eventemitter3';
11
11
  import { RondevuAPI, IceCandidate } from '../api/client.js';
12
12
  export interface PollAnswerEvent {
13
13
  offerId: string;
14
- answererId: string;
14
+ answererPublicKey: string;
15
15
  sdp: string;
16
16
  answeredAt: number;
17
17
  matchedTags?: string[];
@@ -79,7 +79,7 @@ export class PollingManager extends EventEmitter {
79
79
  this.debug(`Poll: answer for ${answer.offerId}`);
80
80
  this.emit('poll:answer', {
81
81
  offerId: answer.offerId,
82
- answererId: answer.answererId,
82
+ answererPublicKey: answer.answererPublicKey,
83
83
  sdp: answer.sdp,
84
84
  answeredAt: answer.answeredAt,
85
85
  matchedTags: answer.matchedTags,
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Contains all public interfaces and types for the Rondevu client.
5
5
  */
6
- import { Credential } from '../api/client.js';
6
+ import { KeyPair } from '../api/client.js';
7
7
  import { CryptoAdapter } from '../crypto/adapter.js';
8
8
  import { WebRTCAdapter } from '../webrtc/adapter.js';
9
9
  import { ConnectionConfig } from '../connections/config.js';
@@ -14,10 +14,8 @@ import { IceServerPreset } from './ice-config.js';
14
14
  export interface RondevuOptions {
15
15
  /** API URL (defaults to 'https://api.ronde.vu') */
16
16
  apiUrl?: string;
17
- /** Pre-existing credential (will generate if not provided) */
18
- credential?: Credential;
19
- /** Claim specific username (4-32 chars, alphanumeric + dashes + periods) */
20
- username?: string;
17
+ /** Pre-existing keypair (will generate if not provided) */
18
+ keyPair?: KeyPair;
21
19
  /** Crypto adapter (defaults to WebCryptoAdapter) */
22
20
  cryptoAdapter?: CryptoAdapter;
23
21
  /** WebRTC adapter (defaults to BrowserWebRTCAdapter, use NodeWebRTCAdapter for Node.js) */
@@ -61,6 +59,8 @@ export interface OfferOptions {
61
59
  connectionConfig?: Partial<ConnectionConfig>;
62
60
  /** Auto-start filling offers (default: true). Set to false to manually call startFilling() */
63
61
  autoStart?: boolean;
62
+ /** Delay in ms between creating each offer during pool filling (default: 100). Helps avoid rate limiting. */
63
+ offerCreationThrottleMs?: number;
64
64
  }
65
65
  /**
66
66
  * Handle returned by rondevu.offer() for controlling the offer lifecycle
@@ -81,8 +81,8 @@ export interface ConnectionContext {
81
81
  tags: string[];
82
82
  /** The offer ID for this connection */
83
83
  offerId: string;
84
- /** Username of the connected peer */
85
- peerUsername: string;
84
+ /** Public key of the connected peer */
85
+ peerPublicKey: string;
86
86
  }
87
87
  /**
88
88
  * Options for rondevu.discover() - discovering available offers
@@ -99,8 +99,8 @@ export interface DiscoverOptions {
99
99
  export interface DiscoveredOffer {
100
100
  /** Unique offer identifier */
101
101
  offerId: string;
102
- /** Username of the offer publisher */
103
- username: string;
102
+ /** Public key of the offer publisher */
103
+ publicKey: string;
104
104
  /** Tags associated with this offer */
105
105
  tags: string[];
106
106
  /** The WebRTC offer SDP */
@@ -1,4 +1,5 @@
1
- import { Credential, IceCandidate } from '../api/client.js';
1
+ import { KeyPair, IceCandidate } from '../api/client.js';
2
+ import { CryptoAdapter } from '../crypto/adapter.js';
2
3
  import { WebRTCAdapter } from '../webrtc/adapter.js';
3
4
  import { EventEmitter } from 'eventemitter3';
4
5
  import { OffererConnection } from '../connections/offerer.js';
@@ -12,6 +13,7 @@ export type { PollAnswerEvent, PollIceEvent } from './polling-manager.js';
12
13
  * Rondevu - Complete WebRTC signaling client with durable connections
13
14
  *
14
15
  * Uses a tags-based discovery system where offers have 1+ tags for matching.
16
+ * Authentication uses Ed25519 public key cryptography - your public key IS your identity.
15
17
  *
16
18
  * @example
17
19
  * ```typescript
@@ -62,7 +64,7 @@ export declare class Rondevu extends EventEmitter {
62
64
  private static readonly POLLING_INTERVAL_MS;
63
65
  private api;
64
66
  private readonly apiUrl;
65
- private credential;
67
+ private keyPair;
66
68
  private cryptoAdapter?;
67
69
  private webrtcAdapter;
68
70
  private iceServers;
@@ -91,25 +93,30 @@ export declare class Rondevu extends EventEmitter {
91
93
  */
92
94
  static connect(options?: RondevuOptions): Promise<Rondevu>;
93
95
  /**
94
- * Get the current credential name
96
+ * Get the current public key (identity)
95
97
  */
96
- getName(): string;
98
+ getPublicKey(): string;
97
99
  /**
98
- * Get the full credential (name + secret)
99
- * Use this to persist credentials for future sessions
100
+ * Get the full keypair (publicKey + privateKey)
101
+ * Use this to persist keypair for future sessions
100
102
  *
101
103
  * ⚠️ SECURITY WARNING:
102
- * - The secret grants full access to this identity
103
- * - Store credentials securely (encrypted storage, never in logs)
104
- * - Never expose credentials in URLs, console output, or error messages
105
- * - Treat the secret like a password or API key
104
+ * - The private key grants full access to this identity
105
+ * - Store keypair securely (encrypted storage, never in logs)
106
+ * - Never expose private key in URLs, console output, or error messages
107
+ * - Treat the private key like a password or API key
106
108
  */
107
- getCredential(): Credential;
109
+ getKeyPair(): KeyPair;
108
110
  /**
109
111
  * Get the WebRTC adapter for creating peer connections
110
112
  * Used internally by offer pool and connections
111
113
  */
112
114
  getWebRTCAdapter(): WebRTCAdapter;
115
+ /**
116
+ * Get the crypto adapter for signing/verification operations
117
+ * Used for meta.json signing and other crypto operations
118
+ */
119
+ getCryptoAdapter(): CryptoAdapter;
113
120
  /**
114
121
  * Default offer factory - creates a simple data channel connection
115
122
  * The RTCPeerConnection is created by Rondevu and passed in
@@ -198,14 +205,14 @@ export declare class Rondevu extends EventEmitter {
198
205
  * // Connect to any peer matching tags
199
206
  * const peer = await rondevu.peer({ tags: ['chat'] })
200
207
  *
201
- * // Connect to specific user
208
+ * // Connect to specific peer by public key
202
209
  * const peer = await rondevu.peer({
203
- * username: 'alice',
210
+ * publicKey: 'abc123...',
204
211
  * tags: ['chat']
205
212
  * })
206
213
  *
207
214
  * peer.on('open', () => {
208
- * console.log('Connected to', peer.peerUsername)
215
+ * console.log('Connected to', peer.peerPublicKey)
209
216
  * peer.send('Hello!')
210
217
  * })
211
218
  *
@@ -243,7 +250,7 @@ export declare class Rondevu extends EventEmitter {
243
250
  *
244
251
  * // Access offers
245
252
  * for (const offer of result.offers) {
246
- * console.log(offer.username, offer.tags)
253
+ * console.log(offer.publicKey, offer.tags)
247
254
  * }
248
255
  * ```
249
256
  */
@@ -261,7 +268,7 @@ export declare class Rondevu extends EventEmitter {
261
268
  getOfferAnswer(offerId: string): Promise<{
262
269
  sdp: string;
263
270
  offerId: string;
264
- answererId: string;
271
+ answererPublicKey: string;
265
272
  answeredAt: number;
266
273
  } | null>;
267
274
  /**
@@ -271,14 +278,14 @@ export declare class Rondevu extends EventEmitter {
271
278
  poll(since?: number): Promise<{
272
279
  answers: Array<{
273
280
  offerId: string;
274
- answererId: string;
281
+ answererPublicKey: string;
275
282
  sdp: string;
276
283
  answeredAt: number;
277
284
  }>;
278
285
  iceCandidates: Record<string, Array<{
279
286
  candidate: RTCIceCandidateInit | null;
280
287
  role: 'offerer' | 'answerer';
281
- peerId: string;
288
+ peerPublicKey: string;
282
289
  createdAt: number;
283
290
  }>>;
284
291
  }>;