@peers-app/peers-sdk 0.23.13 → 0.24.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/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +4 -1
- package/dist/contracts/system/index.d.ts +2 -0
- package/dist/contracts/system/index.js +5 -1
- package/dist/contracts/system/invites-contract.d.ts +121 -0
- package/dist/contracts/system/invites-contract.js +150 -0
- package/dist/data/create-group.d.ts +24 -0
- package/dist/data/create-group.js +61 -0
- package/dist/data/group-secret.d.ts +10 -0
- package/dist/data/group-secret.js +26 -0
- package/dist/data/group-share.d.ts +0 -20
- package/dist/data/group-share.js +0 -35
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.js +2 -0
- package/dist/data/invites.d.ts +350 -0
- package/dist/data/invites.js +99 -0
- package/dist/data/users.js +4 -0
- package/dist/device-pairing/constants.d.ts +27 -9
- package/dist/device-pairing/constants.js +28 -10
- package/dist/device-pairing/cpace.d.ts +46 -0
- package/dist/device-pairing/cpace.js +173 -0
- package/dist/device-pairing/cpace.test.js +116 -0
- package/dist/device-pairing/crypto.d.ts +40 -15
- package/dist/device-pairing/crypto.js +74 -33
- package/dist/device-pairing/device-pairing.test.js +156 -119
- package/dist/device-pairing/index.d.ts +3 -1
- package/dist/device-pairing/index.js +3 -1
- package/dist/device-pairing/pairing-code.d.ts +27 -0
- package/dist/device-pairing/pairing-code.js +84 -0
- package/dist/device-pairing/pgp-word-list.d.ts +14 -0
- package/dist/device-pairing/pgp-word-list.js +531 -0
- package/dist/device-pairing/signaling.d.ts +17 -6
- package/dist/device-pairing/signaling.js +31 -8
- package/dist/device-pairing/transcript.d.ts +3 -3
- package/dist/device-pairing/transcript.js +6 -13
- package/dist/device-pairing/types.d.ts +870 -224
- package/dist/device-pairing/types.js +186 -108
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/invites/data-box-schema.d.ts +15 -0
- package/dist/invites/data-box-schema.js +15 -0
- package/dist/invites/group-approval.d.ts +43 -0
- package/dist/invites/group-approval.js +155 -0
- package/dist/invites/group-approval.test.js +221 -0
- package/dist/invites/index.d.ts +5 -0
- package/dist/{group-invite → invites}/index.js +5 -3
- package/dist/invites/invite-epoch.d.ts +18 -0
- package/dist/invites/invite-epoch.js +35 -0
- package/dist/invites/invite-messages.d.ts +1354 -0
- package/dist/invites/invite-messages.js +145 -0
- package/dist/invites/invite-token.d.ts +334 -0
- package/dist/invites/invite-token.js +178 -0
- package/dist/invites/invite-token.test.d.ts +1 -0
- package/dist/invites/invite-token.test.js +123 -0
- package/dist/rpc-types.d.ts +5 -4
- package/dist/rpc-types.js +2 -1
- package/dist/system-ids.d.ts +22 -0
- package/dist/system-ids.js +23 -1
- package/dist/types/peer-device.d.ts +15 -1
- package/dist/types/peer-device.js +15 -1
- package/package.json +1 -1
- package/dist/device-pairing/invitation.d.ts +0 -13
- package/dist/device-pairing/invitation.js +0 -85
- package/dist/group-invite/group-invite.d.ts +0 -53
- package/dist/group-invite/group-invite.js +0 -231
- package/dist/group-invite/group-invite.pvars.d.ts +0 -94
- package/dist/group-invite/group-invite.pvars.js +0 -139
- package/dist/group-invite/group-invite.types.d.ts +0 -124
- package/dist/group-invite/group-invite.types.js +0 -8
- package/dist/group-invite/index.d.ts +0 -3
- package/dist/user-connect/connection-code.d.ts +0 -67
- package/dist/user-connect/connection-code.js +0 -176
- package/dist/user-connect/connection-code.test.js +0 -213
- package/dist/user-connect/index.d.ts +0 -3
- package/dist/user-connect/index.js +0 -19
- package/dist/user-connect/user-connect.pvars.d.ts +0 -20
- package/dist/user-connect/user-connect.pvars.js +0 -63
- package/dist/user-connect/user-connect.pvars.test.js +0 -46
- package/dist/user-connect/user-connect.types.d.ts +0 -94
- package/dist/user-connect/user-connect.types.js +0 -68
- /package/dist/{user-connect/connection-code.test.d.ts → device-pairing/cpace.test.d.ts} +0 -0
- /package/dist/{user-connect/user-connect.pvars.test.d.ts → invites/group-approval.test.d.ts} +0 -0
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Utilities for group invitation flow.
|
|
4
|
-
*
|
|
5
|
-
* Uses a simple password that users can edit or customize.
|
|
6
|
-
* The password is hashed to create an alias for network discovery.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.generateInvitePassword = generateInvitePassword;
|
|
10
|
-
exports.deriveAliasFromPassword = deriveAliasFromPassword;
|
|
11
|
-
exports.encryptWithPassword = encryptWithPassword;
|
|
12
|
-
exports.decryptWithPassword = decryptWithPassword;
|
|
13
|
-
exports.isValidInvitePassword = isValidInvitePassword;
|
|
14
|
-
exports.generateRequestId = generateRequestId;
|
|
15
|
-
const sha2_1 = require("@noble/hashes/sha2");
|
|
16
|
-
const nacl = require("tweetnacl");
|
|
17
|
-
const tx_encoding_1 = require("../device/tx-encoding");
|
|
18
|
-
const keys_1 = require("../keys");
|
|
19
|
-
/**
|
|
20
|
-
* Word list for generating memorable passphrases.
|
|
21
|
-
* Using common, easy-to-spell words.
|
|
22
|
-
*/
|
|
23
|
-
const WORD_LIST = [
|
|
24
|
-
"apple",
|
|
25
|
-
"banana",
|
|
26
|
-
"cherry",
|
|
27
|
-
"date",
|
|
28
|
-
"elder",
|
|
29
|
-
"fig",
|
|
30
|
-
"grape",
|
|
31
|
-
"honey",
|
|
32
|
-
"iris",
|
|
33
|
-
"jade",
|
|
34
|
-
"kiwi",
|
|
35
|
-
"lemon",
|
|
36
|
-
"mango",
|
|
37
|
-
"nectar",
|
|
38
|
-
"olive",
|
|
39
|
-
"peach",
|
|
40
|
-
"quince",
|
|
41
|
-
"rose",
|
|
42
|
-
"sugar",
|
|
43
|
-
"tulip",
|
|
44
|
-
"umber",
|
|
45
|
-
"violet",
|
|
46
|
-
"willow",
|
|
47
|
-
"yarrow",
|
|
48
|
-
"zebra",
|
|
49
|
-
"amber",
|
|
50
|
-
"blaze",
|
|
51
|
-
"coral",
|
|
52
|
-
"dusk",
|
|
53
|
-
"ember",
|
|
54
|
-
"frost",
|
|
55
|
-
"glow",
|
|
56
|
-
"haze",
|
|
57
|
-
"ivory",
|
|
58
|
-
"jewel",
|
|
59
|
-
"karma",
|
|
60
|
-
"lunar",
|
|
61
|
-
"maple",
|
|
62
|
-
"nova",
|
|
63
|
-
"opal",
|
|
64
|
-
"prism",
|
|
65
|
-
"quest",
|
|
66
|
-
"river",
|
|
67
|
-
"storm",
|
|
68
|
-
"terra",
|
|
69
|
-
"ultra",
|
|
70
|
-
"vivid",
|
|
71
|
-
"winter",
|
|
72
|
-
"azure",
|
|
73
|
-
"brave",
|
|
74
|
-
"calm",
|
|
75
|
-
"dawn",
|
|
76
|
-
"echo",
|
|
77
|
-
"fern",
|
|
78
|
-
"gentle",
|
|
79
|
-
"harbor",
|
|
80
|
-
"island",
|
|
81
|
-
"jungle",
|
|
82
|
-
"kindle",
|
|
83
|
-
"light",
|
|
84
|
-
"meadow",
|
|
85
|
-
"night",
|
|
86
|
-
"ocean",
|
|
87
|
-
"piano",
|
|
88
|
-
"quiet",
|
|
89
|
-
"rain",
|
|
90
|
-
"silk",
|
|
91
|
-
"tide",
|
|
92
|
-
"unity",
|
|
93
|
-
"velvet",
|
|
94
|
-
"wave",
|
|
95
|
-
"zenith",
|
|
96
|
-
"arch",
|
|
97
|
-
"beam",
|
|
98
|
-
"cloud",
|
|
99
|
-
"drift",
|
|
100
|
-
"earth",
|
|
101
|
-
"flame",
|
|
102
|
-
"green",
|
|
103
|
-
"hill",
|
|
104
|
-
"ink",
|
|
105
|
-
"jazz",
|
|
106
|
-
"keen",
|
|
107
|
-
"lake",
|
|
108
|
-
"moon",
|
|
109
|
-
"nest",
|
|
110
|
-
"oak",
|
|
111
|
-
"peak",
|
|
112
|
-
"ruby",
|
|
113
|
-
"sand",
|
|
114
|
-
"tree",
|
|
115
|
-
"vine",
|
|
116
|
-
"wind",
|
|
117
|
-
"year",
|
|
118
|
-
"zone",
|
|
119
|
-
"bold",
|
|
120
|
-
"crisp",
|
|
121
|
-
"deep",
|
|
122
|
-
"fair",
|
|
123
|
-
"glad",
|
|
124
|
-
"warm",
|
|
125
|
-
"pure",
|
|
126
|
-
"safe",
|
|
127
|
-
"true",
|
|
128
|
-
];
|
|
129
|
-
/**
|
|
130
|
-
* Generate a random memorable passphrase.
|
|
131
|
-
* Format: "word-word-word-word" (4 words separated by dashes)
|
|
132
|
-
*
|
|
133
|
-
* @param wordCount Number of words to use (default 4)
|
|
134
|
-
* @returns A memorable passphrase
|
|
135
|
-
*/
|
|
136
|
-
function generateInvitePassword(wordCount = 4) {
|
|
137
|
-
const words = [];
|
|
138
|
-
const bytes = nacl.randomBytes(wordCount);
|
|
139
|
-
for (let i = 0; i < wordCount; i++) {
|
|
140
|
-
const index = bytes[i] % WORD_LIST.length;
|
|
141
|
-
words.push(WORD_LIST[index]);
|
|
142
|
-
}
|
|
143
|
-
return words.join("-");
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Derive a network alias from a password.
|
|
147
|
-
* Uses SHA-256 hash, takes first 8 characters as hex.
|
|
148
|
-
*
|
|
149
|
-
* @param password The password to derive from
|
|
150
|
-
* @returns An 8-character hex alias
|
|
151
|
-
*/
|
|
152
|
-
function deriveAliasFromPassword(password) {
|
|
153
|
-
const normalized = password.toLowerCase().trim();
|
|
154
|
-
const hash = (0, sha2_1.sha256)(new TextEncoder().encode(normalized));
|
|
155
|
-
// Convert first 4 bytes to 8-char hex string
|
|
156
|
-
const hexChars = Array.from(hash.slice(0, 4))
|
|
157
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
158
|
-
.join("")
|
|
159
|
-
.toUpperCase();
|
|
160
|
-
return hexChars;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Derive a 32-byte encryption key from the password.
|
|
164
|
-
* Uses SHA-256 of the password.
|
|
165
|
-
*
|
|
166
|
-
* @param password The password to derive key from
|
|
167
|
-
* @returns A 32-byte encryption key
|
|
168
|
-
*/
|
|
169
|
-
function deriveKeyFromPassword(password) {
|
|
170
|
-
const normalized = password.toLowerCase().trim();
|
|
171
|
-
return (0, sha2_1.sha256)(new TextEncoder().encode(normalized));
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Encrypt data using the invite password.
|
|
175
|
-
* Uses NaCl secretbox with a key derived from the password.
|
|
176
|
-
*
|
|
177
|
-
* @param data Data to encrypt
|
|
178
|
-
* @param password The invitation password
|
|
179
|
-
* @returns Base64-encoded encrypted data with nonce
|
|
180
|
-
*/
|
|
181
|
-
function encryptWithPassword(data, password) {
|
|
182
|
-
const key = deriveKeyFromPassword(password);
|
|
183
|
-
const nonce = nacl.randomBytes(24);
|
|
184
|
-
const dataBytes = (0, tx_encoding_1.txEncode)(data);
|
|
185
|
-
const encrypted = nacl.secretbox(dataBytes, nonce, key);
|
|
186
|
-
// Combine nonce + encrypted data
|
|
187
|
-
const combined = new Uint8Array(nonce.length + encrypted.length);
|
|
188
|
-
combined.set(nonce);
|
|
189
|
-
combined.set(encrypted, nonce.length);
|
|
190
|
-
return (0, keys_1.encodeBase64)(combined);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Decrypt data using the invite password.
|
|
194
|
-
*
|
|
195
|
-
* @param encrypted Base64-encoded encrypted data
|
|
196
|
-
* @param password The invitation password
|
|
197
|
-
* @returns Decrypted data
|
|
198
|
-
*/
|
|
199
|
-
function decryptWithPassword(encrypted, password) {
|
|
200
|
-
const key = deriveKeyFromPassword(password);
|
|
201
|
-
const combined = (0, keys_1.decodeBase64)(encrypted);
|
|
202
|
-
const nonce = combined.slice(0, 24);
|
|
203
|
-
const ciphertext = combined.slice(24);
|
|
204
|
-
const decrypted = nacl.secretbox.open(ciphertext, nonce, key);
|
|
205
|
-
if (!decrypted) {
|
|
206
|
-
throw new Error("Decryption failed - invalid password or corrupted data");
|
|
207
|
-
}
|
|
208
|
-
return (0, tx_encoding_1.txDecode)(decrypted);
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Validate a password format.
|
|
212
|
-
* Passwords should be at least 4 characters.
|
|
213
|
-
*
|
|
214
|
-
* @param password Password to validate
|
|
215
|
-
* @returns true if valid
|
|
216
|
-
*/
|
|
217
|
-
function isValidInvitePassword(password) {
|
|
218
|
-
const trimmed = password.trim();
|
|
219
|
-
return trimmed.length >= 4;
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Generate a unique request ID for tracking join requests.
|
|
223
|
-
*
|
|
224
|
-
* @returns A unique request ID
|
|
225
|
-
*/
|
|
226
|
-
function generateRequestId() {
|
|
227
|
-
const bytes = nacl.randomBytes(8);
|
|
228
|
-
return Array.from(bytes)
|
|
229
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
230
|
-
.join("");
|
|
231
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Persistent variables for group invitation state.
|
|
3
|
-
*
|
|
4
|
-
* These device-level variables track:
|
|
5
|
-
* - Active invitation listeners (admin side)
|
|
6
|
-
* - Pending join requests (admin side)
|
|
7
|
-
* - Status messages for UI feedback
|
|
8
|
-
* - Action triggers (UI sets these, device layer reacts)
|
|
9
|
-
*/
|
|
10
|
-
import type { GroupMemberRole } from "../data/group-member-roles";
|
|
11
|
-
import type { IGroupInviteListener, IGroupInviteListenerInfo, IGroupJoinRequest, IGroupJoinResult } from "./group-invite.types";
|
|
12
|
-
/**
|
|
13
|
-
* Active group invitation listeners.
|
|
14
|
-
* Key is groupId, value contains the password and alias being listened for.
|
|
15
|
-
*
|
|
16
|
-
* Used by admins who are waiting for join requests.
|
|
17
|
-
*/
|
|
18
|
-
export declare const groupInviteListeners: import("../data/persistent-vars").PersistentVar<Record<string, IGroupInviteListener>>;
|
|
19
|
-
/**
|
|
20
|
-
* Pending join requests that need admin approval.
|
|
21
|
-
* Array of requests waiting to be approved or denied.
|
|
22
|
-
*
|
|
23
|
-
* These are shown to the admin in the group details UI.
|
|
24
|
-
*/
|
|
25
|
-
export declare const groupInviteRequests: import("../data/persistent-vars").PersistentVar<IGroupJoinRequest[]>;
|
|
26
|
-
/**
|
|
27
|
-
* Status message for the group invite UI (admin side).
|
|
28
|
-
* Used to show success/error messages to the user.
|
|
29
|
-
*/
|
|
30
|
-
export declare const groupInviteStatus: import("../data/persistent-vars").PersistentVar<string>;
|
|
31
|
-
/**
|
|
32
|
-
* Status message for the join group UI (joiner side).
|
|
33
|
-
* Used to show discovery/join status to the user.
|
|
34
|
-
*/
|
|
35
|
-
export declare const groupJoinStatus: import("../data/persistent-vars").PersistentVar<string>;
|
|
36
|
-
/**
|
|
37
|
-
* Results from discovering group listeners.
|
|
38
|
-
* Updated by the device layer after discovery completes.
|
|
39
|
-
*/
|
|
40
|
-
export declare const groupInviteDiscoveryResult: import("../data/persistent-vars").PersistentVar<IGroupInviteListenerInfo[]>;
|
|
41
|
-
/**
|
|
42
|
-
* Result of joining a group.
|
|
43
|
-
* Updated by the device layer after join completes.
|
|
44
|
-
*/
|
|
45
|
-
export declare const groupInviteJoinResult: import("../data/persistent-vars").PersistentVar<IGroupJoinResult | null>;
|
|
46
|
-
/**
|
|
47
|
-
* Responses to join requests, keyed by requestId.
|
|
48
|
-
* Written by processJoinRequest, read by handleJoinRequest.
|
|
49
|
-
* Entries are cleaned up after being read.
|
|
50
|
-
*/
|
|
51
|
-
export declare const groupInviteResponses: import("../data/persistent-vars").PersistentVar<Record<string, {
|
|
52
|
-
statusCode: number;
|
|
53
|
-
payload: string;
|
|
54
|
-
}>>;
|
|
55
|
-
/**
|
|
56
|
-
* Trigger to start listening for invitations.
|
|
57
|
-
* Set by UI: { groupId, password }
|
|
58
|
-
* Device layer clears after processing.
|
|
59
|
-
*/
|
|
60
|
-
export declare const groupInviteStartListening: import("../data/persistent-vars").PersistentVar<{
|
|
61
|
-
groupId: string;
|
|
62
|
-
password: string;
|
|
63
|
-
} | null>;
|
|
64
|
-
/**
|
|
65
|
-
* Trigger to stop listening for invitations.
|
|
66
|
-
* Set by UI: groupId
|
|
67
|
-
* Device layer clears after processing.
|
|
68
|
-
*/
|
|
69
|
-
export declare const groupInviteStopListening: import("../data/persistent-vars").PersistentVar<string | null>;
|
|
70
|
-
/**
|
|
71
|
-
* Trigger to discover groups with a password.
|
|
72
|
-
* Set by UI: password
|
|
73
|
-
* Device layer updates groupInviteDiscoveryResult and clears this.
|
|
74
|
-
*/
|
|
75
|
-
export declare const groupInviteDiscover: import("../data/persistent-vars").PersistentVar<string | null>;
|
|
76
|
-
/**
|
|
77
|
-
* Trigger to send a join request.
|
|
78
|
-
* Set by UI: { listener, password }
|
|
79
|
-
* Device layer updates groupInviteJoinResult and clears this.
|
|
80
|
-
*/
|
|
81
|
-
export declare const groupInviteSendRequest: import("../data/persistent-vars").PersistentVar<{
|
|
82
|
-
listener: IGroupInviteListenerInfo;
|
|
83
|
-
password: string;
|
|
84
|
-
} | null>;
|
|
85
|
-
/**
|
|
86
|
-
* Trigger to process (approve/deny) a join request.
|
|
87
|
-
* Set by UI: { requestId, approved, role }
|
|
88
|
-
* Device layer processes and clears this.
|
|
89
|
-
*/
|
|
90
|
-
export declare const groupInviteProcessRequest: import("../data/persistent-vars").PersistentVar<{
|
|
91
|
-
requestId: string;
|
|
92
|
-
approved: boolean;
|
|
93
|
-
role: GroupMemberRole;
|
|
94
|
-
} | null>;
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Persistent variables for group invitation state.
|
|
4
|
-
*
|
|
5
|
-
* These device-level variables track:
|
|
6
|
-
* - Active invitation listeners (admin side)
|
|
7
|
-
* - Pending join requests (admin side)
|
|
8
|
-
* - Status messages for UI feedback
|
|
9
|
-
* - Action triggers (UI sets these, device layer reacts)
|
|
10
|
-
*/
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.groupInviteProcessRequest = exports.groupInviteSendRequest = exports.groupInviteDiscover = exports.groupInviteStopListening = exports.groupInviteStartListening = exports.groupInviteResponses = exports.groupInviteJoinResult = exports.groupInviteDiscoveryResult = exports.groupJoinStatus = exports.groupInviteStatus = exports.groupInviteRequests = exports.groupInviteListeners = void 0;
|
|
13
|
-
const persistent_vars_1 = require("../data/persistent-vars");
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// STATE VARIABLES (read by both UI and device layer)
|
|
16
|
-
// ============================================================================
|
|
17
|
-
/**
|
|
18
|
-
* Active group invitation listeners.
|
|
19
|
-
* Key is groupId, value contains the password and alias being listened for.
|
|
20
|
-
*
|
|
21
|
-
* Used by admins who are waiting for join requests.
|
|
22
|
-
*/
|
|
23
|
-
exports.groupInviteListeners = (0, persistent_vars_1.deviceVar)("groupInviteListeners", { defaultValue: {} });
|
|
24
|
-
/**
|
|
25
|
-
* Pending join requests that need admin approval.
|
|
26
|
-
* Array of requests waiting to be approved or denied.
|
|
27
|
-
*
|
|
28
|
-
* These are shown to the admin in the group details UI.
|
|
29
|
-
*/
|
|
30
|
-
exports.groupInviteRequests = (0, persistent_vars_1.deviceVar)("groupInviteRequests", {
|
|
31
|
-
defaultValue: [],
|
|
32
|
-
});
|
|
33
|
-
/**
|
|
34
|
-
* Status message for the group invite UI (admin side).
|
|
35
|
-
* Used to show success/error messages to the user.
|
|
36
|
-
*/
|
|
37
|
-
exports.groupInviteStatus = (0, persistent_vars_1.deviceVar)("groupInviteStatus", { defaultValue: "" });
|
|
38
|
-
/**
|
|
39
|
-
* Status message for the join group UI (joiner side).
|
|
40
|
-
* Used to show discovery/join status to the user.
|
|
41
|
-
*/
|
|
42
|
-
exports.groupJoinStatus = (0, persistent_vars_1.deviceVar)("groupJoinStatus", { defaultValue: "" });
|
|
43
|
-
/**
|
|
44
|
-
* Results from discovering group listeners.
|
|
45
|
-
* Updated by the device layer after discovery completes.
|
|
46
|
-
*/
|
|
47
|
-
exports.groupInviteDiscoveryResult = (0, persistent_vars_1.deviceVar)("groupInviteDiscoveryResult", { defaultValue: [] });
|
|
48
|
-
/**
|
|
49
|
-
* Result of joining a group.
|
|
50
|
-
* Updated by the device layer after join completes.
|
|
51
|
-
*/
|
|
52
|
-
exports.groupInviteJoinResult = (0, persistent_vars_1.deviceVar)("groupInviteJoinResult", {
|
|
53
|
-
defaultValue: null,
|
|
54
|
-
});
|
|
55
|
-
/**
|
|
56
|
-
* Responses to join requests, keyed by requestId.
|
|
57
|
-
* Written by processJoinRequest, read by handleJoinRequest.
|
|
58
|
-
* Entries are cleaned up after being read.
|
|
59
|
-
*/
|
|
60
|
-
exports.groupInviteResponses = (0, persistent_vars_1.deviceVar)("groupInviteResponses", { defaultValue: {} });
|
|
61
|
-
// ============================================================================
|
|
62
|
-
// ACTION TRIGGERS (UI sets these, device layer reacts)
|
|
63
|
-
// ============================================================================
|
|
64
|
-
/**
|
|
65
|
-
* Trigger to start listening for invitations.
|
|
66
|
-
* Set by UI: { groupId, password }
|
|
67
|
-
* Device layer clears after processing.
|
|
68
|
-
*/
|
|
69
|
-
exports.groupInviteStartListening = (0, persistent_vars_1.deviceVar)("groupInviteStartListening", { defaultValue: null });
|
|
70
|
-
/**
|
|
71
|
-
* Trigger to stop listening for invitations.
|
|
72
|
-
* Set by UI: groupId
|
|
73
|
-
* Device layer clears after processing.
|
|
74
|
-
*/
|
|
75
|
-
exports.groupInviteStopListening = (0, persistent_vars_1.deviceVar)("groupInviteStopListening", {
|
|
76
|
-
defaultValue: null,
|
|
77
|
-
});
|
|
78
|
-
/**
|
|
79
|
-
* Trigger to discover groups with a password.
|
|
80
|
-
* Set by UI: password
|
|
81
|
-
* Device layer updates groupInviteDiscoveryResult and clears this.
|
|
82
|
-
*/
|
|
83
|
-
exports.groupInviteDiscover = (0, persistent_vars_1.deviceVar)("groupInviteDiscover", {
|
|
84
|
-
defaultValue: null,
|
|
85
|
-
});
|
|
86
|
-
/**
|
|
87
|
-
* Trigger to send a join request.
|
|
88
|
-
* Set by UI: { listener, password }
|
|
89
|
-
* Device layer updates groupInviteJoinResult and clears this.
|
|
90
|
-
*/
|
|
91
|
-
exports.groupInviteSendRequest = (0, persistent_vars_1.deviceVar)("groupInviteSendRequest", { defaultValue: null });
|
|
92
|
-
/**
|
|
93
|
-
* Trigger to process (approve/deny) a join request.
|
|
94
|
-
* Set by UI: { requestId, approved, role }
|
|
95
|
-
* Device layer processes and clears this.
|
|
96
|
-
*/
|
|
97
|
-
exports.groupInviteProcessRequest = (0, persistent_vars_1.deviceVar)("groupInviteProcessRequest", { defaultValue: null });
|
|
98
|
-
// ============================================================================
|
|
99
|
-
// AUTO-CLEANUP
|
|
100
|
-
// ============================================================================
|
|
101
|
-
// Auto-cleanup of expired listeners (10 minute expiry)
|
|
102
|
-
exports.groupInviteListeners.loadingPromise.then(() => {
|
|
103
|
-
// Check every minute for expired listeners
|
|
104
|
-
setInterval(() => {
|
|
105
|
-
const listeners = (0, exports.groupInviteListeners)();
|
|
106
|
-
const now = Date.now();
|
|
107
|
-
const expiredGroupIds = [];
|
|
108
|
-
for (const [groupId, listener] of Object.entries(listeners)) {
|
|
109
|
-
const startedAt = new Date(listener.startedAt).getTime();
|
|
110
|
-
const elapsed = now - startedAt;
|
|
111
|
-
// Expire after 10 minutes
|
|
112
|
-
if (elapsed > 600_000) {
|
|
113
|
-
expiredGroupIds.push(groupId);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (expiredGroupIds.length > 0) {
|
|
117
|
-
const updated = { ...listeners };
|
|
118
|
-
for (const groupId of expiredGroupIds) {
|
|
119
|
-
delete updated[groupId];
|
|
120
|
-
}
|
|
121
|
-
(0, exports.groupInviteListeners)(updated);
|
|
122
|
-
}
|
|
123
|
-
}, 60_000);
|
|
124
|
-
});
|
|
125
|
-
// Clean up old requests after 10 minutes
|
|
126
|
-
exports.groupInviteRequests.loadingPromise.then(() => {
|
|
127
|
-
setInterval(() => {
|
|
128
|
-
const requests = (0, exports.groupInviteRequests)();
|
|
129
|
-
const now = Date.now();
|
|
130
|
-
const validRequests = requests.filter((req) => {
|
|
131
|
-
const receivedAt = new Date(req.receivedAt).getTime();
|
|
132
|
-
const elapsed = now - receivedAt;
|
|
133
|
-
return elapsed < 600_000; // Keep for 10 minutes
|
|
134
|
-
});
|
|
135
|
-
if (validRequests.length !== requests.length) {
|
|
136
|
-
(0, exports.groupInviteRequests)(validRequests);
|
|
137
|
-
}
|
|
138
|
-
}, 60_000);
|
|
139
|
-
});
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Types for the group invitation flow.
|
|
3
|
-
*
|
|
4
|
-
* This flow allows admins to invite new members to a group using a simple
|
|
5
|
-
* password. Multiple admins can listen for the same password simultaneously.
|
|
6
|
-
*/
|
|
7
|
-
import type { GroupMemberRole } from "../data/group-member-roles";
|
|
8
|
-
import type { IGroupMember } from "../data/group-members";
|
|
9
|
-
import type { IGroupShare } from "../data/group-share";
|
|
10
|
-
/**
|
|
11
|
-
* Information about a group shared during the invitation flow.
|
|
12
|
-
*/
|
|
13
|
-
export interface IGroupInviteGroupInfo {
|
|
14
|
-
groupId: string;
|
|
15
|
-
name: string;
|
|
16
|
-
description?: string;
|
|
17
|
-
iconClassName?: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Information about the inviting admin shared during discovery.
|
|
21
|
-
*/
|
|
22
|
-
export interface IGroupInviteListenerInfo {
|
|
23
|
-
/** The admin's user ID */
|
|
24
|
-
userId: string;
|
|
25
|
-
/** The admin's display name */
|
|
26
|
-
name: string;
|
|
27
|
-
/** The admin's device ID */
|
|
28
|
-
deviceId: string;
|
|
29
|
-
/** Group information */
|
|
30
|
-
group: IGroupInviteGroupInfo;
|
|
31
|
-
/** The alias being listened on */
|
|
32
|
-
alias: string;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Information sent by a user requesting to join a group.
|
|
36
|
-
*/
|
|
37
|
-
export interface IGroupJoinRequestInfo {
|
|
38
|
-
/** Requesting user's ID */
|
|
39
|
-
userId: string;
|
|
40
|
-
/** Requesting user's display name */
|
|
41
|
-
name?: string;
|
|
42
|
-
/** Requesting user's public signing key */
|
|
43
|
-
publicKey: string;
|
|
44
|
-
/** Requesting user's public box key for encryption */
|
|
45
|
-
publicBoxKey: string;
|
|
46
|
-
/** Requesting user's device ID */
|
|
47
|
-
deviceId: string;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* A pending join request shown to the admin.
|
|
51
|
-
*/
|
|
52
|
-
export interface IGroupJoinRequest {
|
|
53
|
-
/** Unique request ID */
|
|
54
|
-
requestId: string;
|
|
55
|
-
/** The group being joined */
|
|
56
|
-
groupId: string;
|
|
57
|
-
/** The alias this request came in on */
|
|
58
|
-
alias: string;
|
|
59
|
-
/** The password used (for encryption) */
|
|
60
|
-
password: string;
|
|
61
|
-
/** Information about the requesting user */
|
|
62
|
-
requester: IGroupJoinRequestInfo;
|
|
63
|
-
/** When the request was received */
|
|
64
|
-
receivedAt: Date;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Response sent by admin when approving a join request.
|
|
68
|
-
*/
|
|
69
|
-
export interface IGroupJoinApprovalInfo {
|
|
70
|
-
/** Whether the request was approved */
|
|
71
|
-
approved: boolean;
|
|
72
|
-
/** The role assigned to the new member (if approved) */
|
|
73
|
-
role?: GroupMemberRole;
|
|
74
|
-
/** Canonical group record plus Admin+ seed memberships */
|
|
75
|
-
groupShare?: IGroupShare;
|
|
76
|
-
/** Newly signed membership for the joiner, including Reader roles */
|
|
77
|
-
newMember?: IGroupMember;
|
|
78
|
-
/** Admin's user info for adding to contacts */
|
|
79
|
-
adminInfo?: IGroupJoinRequestInfo;
|
|
80
|
-
/** Error message if rejected */
|
|
81
|
-
rejectionReason?: string;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Signal that a joiner finished importing approval data and can connect to the admin.
|
|
85
|
-
*/
|
|
86
|
-
export interface IGroupInviteJoinFinished {
|
|
87
|
-
/** Group the joiner just imported */
|
|
88
|
-
groupId: string;
|
|
89
|
-
/** Approving admin device that should be contacted in that group context */
|
|
90
|
-
adminDeviceId: string;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Active invitation listener state.
|
|
94
|
-
*/
|
|
95
|
-
export interface IGroupInviteListener {
|
|
96
|
-
/** The group being invited to */
|
|
97
|
-
groupId: string;
|
|
98
|
-
/** The password being listened for */
|
|
99
|
-
password: string;
|
|
100
|
-
/** The derived alias for network discovery */
|
|
101
|
-
alias: string;
|
|
102
|
-
/** When listening started */
|
|
103
|
-
startedAt: Date;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Result of discovering group invite listeners.
|
|
107
|
-
*/
|
|
108
|
-
export interface IGroupInviteDiscoveryResult {
|
|
109
|
-
/** List of listeners found */
|
|
110
|
-
listeners: IGroupInviteListenerInfo[];
|
|
111
|
-
/** Any error that occurred */
|
|
112
|
-
error?: string;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Result of a join request.
|
|
116
|
-
*/
|
|
117
|
-
export interface IGroupJoinResult {
|
|
118
|
-
/** Whether the join was successful */
|
|
119
|
-
success: boolean;
|
|
120
|
-
/** The group ID if successful */
|
|
121
|
-
groupId?: string;
|
|
122
|
-
/** Error message if failed */
|
|
123
|
-
error?: string;
|
|
124
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Types for the group invitation flow.
|
|
4
|
-
*
|
|
5
|
-
* This flow allows admins to invite new members to a group using a simple
|
|
6
|
-
* password. Multiple admins can listen for the same password simultaneously.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilities for generating and handling connection codes.
|
|
3
|
-
*
|
|
4
|
-
* Connection codes are 12 characters of Crockford Base32:
|
|
5
|
-
* - First 4 chars: device alias (rendezvous point)
|
|
6
|
-
* - Last 8 chars: shared secret for encryption
|
|
7
|
-
*
|
|
8
|
-
* Displayed format: XXXX-YYYY-ZZZZ
|
|
9
|
-
*/
|
|
10
|
-
import type { IUserConnectInfo } from "./user-connect.types";
|
|
11
|
-
export interface IConnectionCode {
|
|
12
|
-
/** Full 12-character code */
|
|
13
|
-
code: string;
|
|
14
|
-
/** First 4 characters - device alias for rendezvous */
|
|
15
|
-
alias: string;
|
|
16
|
-
/** Last 8 characters - shared secret for encryption */
|
|
17
|
-
secret: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Generate a new connection code.
|
|
21
|
-
* @returns Object with full code, alias, and secret
|
|
22
|
-
*/
|
|
23
|
-
export declare function generateConnectionCode(): IConnectionCode;
|
|
24
|
-
/**
|
|
25
|
-
* Format a connection code for display.
|
|
26
|
-
* @param code 12-character code
|
|
27
|
-
* @returns Formatted as "XXXX-YYYY-ZZZZ"
|
|
28
|
-
*/
|
|
29
|
-
export declare function formatConnectionCode(code: string): string;
|
|
30
|
-
/**
|
|
31
|
-
* Parse a formatted connection code.
|
|
32
|
-
* @param formatted Code in any format (with or without dashes)
|
|
33
|
-
* @returns Object with alias and secret
|
|
34
|
-
*/
|
|
35
|
-
export declare function parseConnectionCode(formatted: string): {
|
|
36
|
-
code: string;
|
|
37
|
-
alias: string;
|
|
38
|
-
secret: string;
|
|
39
|
-
};
|
|
40
|
-
/**
|
|
41
|
-
* Encrypt data using the shared secret.
|
|
42
|
-
* @param data Data to encrypt
|
|
43
|
-
* @param secret 8-character shared secret
|
|
44
|
-
* @returns Base64-encoded encrypted data with nonce
|
|
45
|
-
*/
|
|
46
|
-
export declare function encryptWithSecret(data: any, secret: string): string;
|
|
47
|
-
/**
|
|
48
|
-
* Decrypt data using the shared secret.
|
|
49
|
-
* @param encrypted Base64-encoded encrypted data
|
|
50
|
-
* @param secret 8-character shared secret
|
|
51
|
-
* @returns Decrypted data
|
|
52
|
-
*/
|
|
53
|
-
export declare function decryptWithSecret(encrypted: string, secret: string): any;
|
|
54
|
-
/**
|
|
55
|
-
* Generate a confirmation hash from both users' information.
|
|
56
|
-
* Both parties should see the same hash if the exchange was successful.
|
|
57
|
-
* @param userA First user's info
|
|
58
|
-
* @param userB Second user's info
|
|
59
|
-
* @returns 4-character confirmation code
|
|
60
|
-
*/
|
|
61
|
-
export declare function generateConfirmationHash(userA: IUserConnectInfo, userB: IUserConnectInfo): string;
|
|
62
|
-
/**
|
|
63
|
-
* Validate that a string is a valid connection code format.
|
|
64
|
-
* @param code Code to validate (with or without dashes)
|
|
65
|
-
* @returns true if valid
|
|
66
|
-
*/
|
|
67
|
-
export declare function isValidConnectionCode(code: string): boolean;
|