@xtr-dev/rondevu-client 0.7.12 → 0.8.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.
@@ -0,0 +1,147 @@
1
+ import * as ed25519 from '@noble/ed25519';
2
+ /**
3
+ * Convert Uint8Array to base64 string
4
+ */
5
+ function bytesToBase64(bytes) {
6
+ const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join('');
7
+ return btoa(binString);
8
+ }
9
+ /**
10
+ * Convert base64 string to Uint8Array
11
+ */
12
+ function base64ToBytes(base64) {
13
+ const binString = atob(base64);
14
+ return Uint8Array.from(binString, (char) => char.codePointAt(0));
15
+ }
16
+ /**
17
+ * Rondevu Username API
18
+ * Handles username claiming with Ed25519 cryptographic proof
19
+ */
20
+ export class RondevuUsername {
21
+ constructor(baseUrl) {
22
+ this.baseUrl = baseUrl;
23
+ }
24
+ /**
25
+ * Generates an Ed25519 keypair for username claiming
26
+ */
27
+ async generateKeypair() {
28
+ const privateKey = ed25519.utils.randomSecretKey();
29
+ const publicKey = await ed25519.getPublicKey(privateKey);
30
+ return {
31
+ publicKey: bytesToBase64(publicKey),
32
+ privateKey: bytesToBase64(privateKey)
33
+ };
34
+ }
35
+ /**
36
+ * Signs a message with an Ed25519 private key
37
+ */
38
+ async signMessage(message, privateKeyBase64) {
39
+ const privateKey = base64ToBytes(privateKeyBase64);
40
+ const encoder = new TextEncoder();
41
+ const messageBytes = encoder.encode(message);
42
+ const signature = await ed25519.sign(messageBytes, privateKey);
43
+ return bytesToBase64(signature);
44
+ }
45
+ /**
46
+ * Claims a username
47
+ * Generates a new keypair if one is not provided
48
+ */
49
+ async claimUsername(username, existingKeypair) {
50
+ // Generate or use existing keypair
51
+ const keypair = existingKeypair || await this.generateKeypair();
52
+ // Create signed message
53
+ const timestamp = Date.now();
54
+ const message = `claim:${username}:${timestamp}`;
55
+ const signature = await this.signMessage(message, keypair.privateKey);
56
+ // Send claim request
57
+ const response = await fetch(`${this.baseUrl}/usernames/claim`, {
58
+ method: 'POST',
59
+ headers: { 'Content-Type': 'application/json' },
60
+ body: JSON.stringify({
61
+ username,
62
+ publicKey: keypair.publicKey,
63
+ signature,
64
+ message
65
+ })
66
+ });
67
+ if (!response.ok) {
68
+ const error = await response.json();
69
+ throw new Error(error.error || 'Failed to claim username');
70
+ }
71
+ const data = await response.json();
72
+ return {
73
+ username: data.username,
74
+ publicKey: keypair.publicKey,
75
+ privateKey: keypair.privateKey,
76
+ claimedAt: data.claimedAt,
77
+ expiresAt: data.expiresAt
78
+ };
79
+ }
80
+ /**
81
+ * Checks if a username is available
82
+ */
83
+ async checkUsername(username) {
84
+ const response = await fetch(`${this.baseUrl}/usernames/${username}`);
85
+ if (!response.ok) {
86
+ throw new Error('Failed to check username');
87
+ }
88
+ const data = await response.json();
89
+ return {
90
+ username: data.username,
91
+ available: data.available,
92
+ claimedAt: data.claimedAt,
93
+ expiresAt: data.expiresAt,
94
+ publicKey: data.publicKey
95
+ };
96
+ }
97
+ /**
98
+ * Helper: Save keypair to localStorage
99
+ * WARNING: This stores the private key in localStorage which is not the most secure
100
+ * For production use, consider using IndexedDB with encryption or hardware security modules
101
+ */
102
+ saveKeypairToStorage(username, publicKey, privateKey) {
103
+ const data = { username, publicKey, privateKey, savedAt: Date.now() };
104
+ localStorage.setItem(`rondevu:keypair:${username}`, JSON.stringify(data));
105
+ }
106
+ /**
107
+ * Helper: Load keypair from localStorage
108
+ */
109
+ loadKeypairFromStorage(username) {
110
+ const stored = localStorage.getItem(`rondevu:keypair:${username}`);
111
+ if (!stored)
112
+ return null;
113
+ try {
114
+ const data = JSON.parse(stored);
115
+ return { publicKey: data.publicKey, privateKey: data.privateKey };
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ /**
122
+ * Helper: Delete keypair from localStorage
123
+ */
124
+ deleteKeypairFromStorage(username) {
125
+ localStorage.removeItem(`rondevu:keypair:${username}`);
126
+ }
127
+ /**
128
+ * Export keypair as JSON string (for backup)
129
+ */
130
+ exportKeypair(publicKey, privateKey) {
131
+ return JSON.stringify({
132
+ publicKey,
133
+ privateKey,
134
+ exportedAt: Date.now()
135
+ });
136
+ }
137
+ /**
138
+ * Import keypair from JSON string
139
+ */
140
+ importKeypair(json) {
141
+ const data = JSON.parse(json);
142
+ if (!data.publicKey || !data.privateKey) {
143
+ throw new Error('Invalid keypair format');
144
+ }
145
+ return { publicKey: data.publicKey, privateKey: data.privateKey };
146
+ }
147
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xtr-dev/rondevu-client",
3
- "version": "0.7.12",
4
- "description": "TypeScript client for Rondevu topic-based peer discovery and signaling server",
3
+ "version": "0.8.0",
4
+ "description": "TypeScript client for Rondevu DNS-like WebRTC with username claiming and service discovery",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -27,6 +27,6 @@
27
27
  "README.md"
28
28
  ],
29
29
  "dependencies": {
30
- "@xtr-dev/rondevu-client": "^0.5.1"
30
+ "@noble/ed25519": "^3.0.0"
31
31
  }
32
32
  }