@peers-app/peers-sdk 0.23.14 → 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.
Files changed (80) hide show
  1. package/dist/contracts/index.d.ts +1 -0
  2. package/dist/contracts/index.js +4 -1
  3. package/dist/contracts/system/index.d.ts +2 -0
  4. package/dist/contracts/system/index.js +5 -1
  5. package/dist/contracts/system/invites-contract.d.ts +121 -0
  6. package/dist/contracts/system/invites-contract.js +150 -0
  7. package/dist/data/create-group.d.ts +24 -0
  8. package/dist/data/create-group.js +61 -0
  9. package/dist/data/group-secret.d.ts +10 -0
  10. package/dist/data/group-secret.js +26 -0
  11. package/dist/data/group-share.d.ts +0 -20
  12. package/dist/data/group-share.js +0 -35
  13. package/dist/data/index.d.ts +2 -0
  14. package/dist/data/index.js +2 -0
  15. package/dist/data/invites.d.ts +350 -0
  16. package/dist/data/invites.js +99 -0
  17. package/dist/data/users.js +4 -0
  18. package/dist/device-pairing/constants.d.ts +27 -9
  19. package/dist/device-pairing/constants.js +28 -10
  20. package/dist/device-pairing/cpace.d.ts +46 -0
  21. package/dist/device-pairing/cpace.js +173 -0
  22. package/dist/device-pairing/cpace.test.js +116 -0
  23. package/dist/device-pairing/crypto.d.ts +40 -15
  24. package/dist/device-pairing/crypto.js +74 -33
  25. package/dist/device-pairing/device-pairing.test.js +156 -119
  26. package/dist/device-pairing/index.d.ts +3 -1
  27. package/dist/device-pairing/index.js +3 -1
  28. package/dist/device-pairing/pairing-code.d.ts +27 -0
  29. package/dist/device-pairing/pairing-code.js +84 -0
  30. package/dist/device-pairing/pgp-word-list.d.ts +14 -0
  31. package/dist/device-pairing/pgp-word-list.js +531 -0
  32. package/dist/device-pairing/signaling.d.ts +17 -6
  33. package/dist/device-pairing/signaling.js +31 -8
  34. package/dist/device-pairing/transcript.d.ts +3 -3
  35. package/dist/device-pairing/transcript.js +6 -13
  36. package/dist/device-pairing/types.d.ts +870 -224
  37. package/dist/device-pairing/types.js +186 -108
  38. package/dist/index.d.ts +1 -2
  39. package/dist/index.js +1 -2
  40. package/dist/invites/data-box-schema.d.ts +15 -0
  41. package/dist/invites/data-box-schema.js +15 -0
  42. package/dist/invites/group-approval.d.ts +43 -0
  43. package/dist/invites/group-approval.js +155 -0
  44. package/dist/invites/group-approval.test.js +221 -0
  45. package/dist/invites/index.d.ts +5 -0
  46. package/dist/{group-invite → invites}/index.js +5 -3
  47. package/dist/invites/invite-epoch.d.ts +18 -0
  48. package/dist/invites/invite-epoch.js +35 -0
  49. package/dist/invites/invite-messages.d.ts +1354 -0
  50. package/dist/invites/invite-messages.js +145 -0
  51. package/dist/invites/invite-token.d.ts +334 -0
  52. package/dist/invites/invite-token.js +178 -0
  53. package/dist/invites/invite-token.test.d.ts +1 -0
  54. package/dist/invites/invite-token.test.js +123 -0
  55. package/dist/rpc-types.d.ts +5 -4
  56. package/dist/rpc-types.js +2 -1
  57. package/dist/system-ids.d.ts +22 -0
  58. package/dist/system-ids.js +23 -1
  59. package/package.json +1 -1
  60. package/dist/device-pairing/invitation.d.ts +0 -13
  61. package/dist/device-pairing/invitation.js +0 -85
  62. package/dist/group-invite/group-invite.d.ts +0 -53
  63. package/dist/group-invite/group-invite.js +0 -231
  64. package/dist/group-invite/group-invite.pvars.d.ts +0 -94
  65. package/dist/group-invite/group-invite.pvars.js +0 -139
  66. package/dist/group-invite/group-invite.types.d.ts +0 -124
  67. package/dist/group-invite/group-invite.types.js +0 -8
  68. package/dist/group-invite/index.d.ts +0 -3
  69. package/dist/user-connect/connection-code.d.ts +0 -67
  70. package/dist/user-connect/connection-code.js +0 -176
  71. package/dist/user-connect/connection-code.test.js +0 -213
  72. package/dist/user-connect/index.d.ts +0 -3
  73. package/dist/user-connect/index.js +0 -19
  74. package/dist/user-connect/user-connect.pvars.d.ts +0 -20
  75. package/dist/user-connect/user-connect.pvars.js +0 -63
  76. package/dist/user-connect/user-connect.pvars.test.js +0 -46
  77. package/dist/user-connect/user-connect.types.d.ts +0 -94
  78. package/dist/user-connect/user-connect.types.js +0 -68
  79. /package/dist/{user-connect/connection-code.test.d.ts → device-pairing/cpace.test.d.ts} +0 -0
  80. /package/dist/{user-connect/user-connect.pvars.test.d.ts → invites/group-approval.test.d.ts} +0 -0
@@ -0,0 +1,350 @@
1
+ import { z } from "zod";
2
+ import type { DataContext } from "../context/data-context";
3
+ import { GroupMemberRole } from "./group-member-roles";
4
+ /** Lifecycle of an invite row. */
5
+ export declare const inviteStatusSchema: z.ZodEnum<["pending", "accepted", "declined", "expired", "revoked"]>;
6
+ /** Lifecycle of an invite row. */
7
+ export type InviteStatus = z.infer<typeof inviteStatusSchema>;
8
+ /**
9
+ * How the invite was issued:
10
+ * - `direct`: a known contact was invited to a group (3a); the payload is the approval.
11
+ * - `approve`: group link whose join requests the inviter approves (3b).
12
+ * - `open`: group link any Admin+ member auto-approves (3c).
13
+ * - `profile`: the reusable contact QR on the Account screen.
14
+ * - `contact`: a one-off contact token.
15
+ */
16
+ export declare const inviteModeSchema: z.ZodEnum<["direct", "approve", "open", "profile", "contact"]>;
17
+ /** How the invite was issued. */
18
+ export type InviteMode = z.infer<typeof inviteModeSchema>;
19
+ /** Outbound delivery progress; `queued` rows are retried when connectivity changes. */
20
+ export declare const inviteDeliveryStateSchema: z.ZodEnum<["queued", "sent"]>;
21
+ /**
22
+ * One pending or historical invite, synced across the user's devices.
23
+ *
24
+ * Boxed payloads are encrypted to the user's box key, which every one of their devices
25
+ * holds, so storing them in this personal-context table is safe. Rows move to a terminal
26
+ * status rather than being deleted; terminal rows older than 30 days are purged.
27
+ */
28
+ export declare const inviteSchema: z.ZodObject<{
29
+ inviteId: z.ZodEffects<z.ZodString, string, string>;
30
+ direction: z.ZodEnum<["in", "out"]>;
31
+ type: z.ZodEnum<["contact", "group"]>;
32
+ status: z.ZodDefault<z.ZodEnum<["pending", "accepted", "declined", "expired", "revoked"]>>;
33
+ mode: z.ZodEnum<["direct", "approve", "open", "profile", "contact"]>;
34
+ /** The other party, once known (unknown for outstanding link invites). */
35
+ counterpart: z.ZodOptional<z.ZodObject<{
36
+ userId: z.ZodEffects<z.ZodString, string, string>;
37
+ publicKey: z.ZodString;
38
+ publicBoxKey: z.ZodString;
39
+ displayName: z.ZodOptional<z.ZodString>;
40
+ }, "strict", z.ZodTypeAny, {
41
+ publicKey: string;
42
+ publicBoxKey: string;
43
+ userId: string;
44
+ displayName?: string | undefined;
45
+ }, {
46
+ publicKey: string;
47
+ publicBoxKey: string;
48
+ userId: string;
49
+ displayName?: string | undefined;
50
+ }>>;
51
+ /** Device the counterpart used, when they told us; lets the network layer connect early. */
52
+ counterpartDeviceId: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
53
+ /**
54
+ * Correlates replies with this row: the token nonce for link invites, a fresh nonce for
55
+ * direct invites.
56
+ */
57
+ nonce: z.ZodOptional<z.ZodString>;
58
+ groupId: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
59
+ groupName: z.ZodOptional<z.ZodString>;
60
+ role: z.ZodOptional<z.ZodNativeEnum<typeof GroupMemberRole>>;
61
+ /** The signed token: what I issued (out) or what I received/used (in). */
62
+ token: z.ZodOptional<z.ZodObject<{
63
+ v: z.ZodLiteral<1>;
64
+ type: z.ZodEnum<["contact", "group"]>;
65
+ inviter: z.ZodObject<{
66
+ userId: z.ZodEffects<z.ZodString, string, string>;
67
+ publicKey: z.ZodString;
68
+ publicBoxKey: z.ZodString;
69
+ displayName: z.ZodOptional<z.ZodString>;
70
+ }, "strict", z.ZodTypeAny, {
71
+ publicKey: string;
72
+ publicBoxKey: string;
73
+ userId: string;
74
+ displayName?: string | undefined;
75
+ }, {
76
+ publicKey: string;
77
+ publicBoxKey: string;
78
+ userId: string;
79
+ displayName?: string | undefined;
80
+ }>;
81
+ expiresAt: z.ZodString;
82
+ nonce: z.ZodString;
83
+ epoch: z.ZodNumber;
84
+ uses: z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<"unlimited">]>;
85
+ group: z.ZodOptional<z.ZodObject<{
86
+ groupId: z.ZodEffects<z.ZodString, string, string>;
87
+ name: z.ZodString;
88
+ publicKey: z.ZodString;
89
+ publicBoxKey: z.ZodString;
90
+ role: z.ZodNativeEnum<typeof GroupMemberRole>;
91
+ mode: z.ZodEnum<["approve", "open"]>;
92
+ }, "strict", z.ZodTypeAny, {
93
+ name: string;
94
+ role: GroupMemberRole;
95
+ publicKey: string;
96
+ groupId: string;
97
+ publicBoxKey: string;
98
+ mode: "open" | "approve";
99
+ }, {
100
+ name: string;
101
+ role: GroupMemberRole;
102
+ publicKey: string;
103
+ groupId: string;
104
+ publicBoxKey: string;
105
+ mode: "open" | "approve";
106
+ }>>;
107
+ signature: z.ZodString;
108
+ }, "strict", z.ZodTypeAny, {
109
+ type: "group" | "contact";
110
+ nonce: string;
111
+ signature: string;
112
+ expiresAt: string;
113
+ v: 1;
114
+ inviter: {
115
+ publicKey: string;
116
+ publicBoxKey: string;
117
+ userId: string;
118
+ displayName?: string | undefined;
119
+ };
120
+ epoch: number;
121
+ uses: 1 | "unlimited";
122
+ group?: {
123
+ name: string;
124
+ role: GroupMemberRole;
125
+ publicKey: string;
126
+ groupId: string;
127
+ publicBoxKey: string;
128
+ mode: "open" | "approve";
129
+ } | undefined;
130
+ }, {
131
+ type: "group" | "contact";
132
+ nonce: string;
133
+ signature: string;
134
+ expiresAt: string;
135
+ v: 1;
136
+ inviter: {
137
+ publicKey: string;
138
+ publicBoxKey: string;
139
+ userId: string;
140
+ displayName?: string | undefined;
141
+ };
142
+ epoch: number;
143
+ uses: 1 | "unlimited";
144
+ group?: {
145
+ name: string;
146
+ role: GroupMemberRole;
147
+ publicKey: string;
148
+ groupId: string;
149
+ publicBoxKey: string;
150
+ mode: "open" | "approve";
151
+ } | undefined;
152
+ }>>;
153
+ /**
154
+ * For `in` direct invites: the GroupApproval boxed to my own user box key. For rows whose
155
+ * `deliveryState` is `queued`: the outbound box (already sealed to `recipientUserId`).
156
+ */
157
+ payload: z.ZodOptional<z.ZodObject<{
158
+ contents: z.ZodString;
159
+ nonce: z.ZodString;
160
+ fromPublicKey: z.ZodString;
161
+ }, "strict", z.ZodTypeAny, {
162
+ nonce: string;
163
+ contents: string;
164
+ fromPublicKey: string;
165
+ }, {
166
+ nonce: string;
167
+ contents: string;
168
+ fromPublicKey: string;
169
+ }>>;
170
+ /** Inviter's epoch at issue time. */
171
+ epoch: z.ZodNumber;
172
+ /** For `in` rows spawned by one of my reusable `out` tokens. */
173
+ parentInviteId: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
174
+ deliveryState: z.ZodOptional<z.ZodEnum<["queued", "sent"]>>;
175
+ /** Outbound recipient for `direct` invites and replies that are still queued. */
176
+ recipientUserId: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
177
+ createdAt: z.ZodDefault<z.ZodDate>;
178
+ expiresAt: z.ZodDate;
179
+ resolvedAt: z.ZodOptional<z.ZodDate>;
180
+ note: z.ZodOptional<z.ZodString>;
181
+ }, "strip", z.ZodTypeAny, {
182
+ type: "group" | "contact";
183
+ direction: "in" | "out";
184
+ status: "pending" | "expired" | "accepted" | "declined" | "revoked";
185
+ expiresAt: Date;
186
+ createdAt: Date;
187
+ mode: "direct" | "open" | "approve" | "contact" | "profile";
188
+ epoch: number;
189
+ inviteId: string;
190
+ role?: GroupMemberRole | undefined;
191
+ nonce?: string | undefined;
192
+ groupId?: string | undefined;
193
+ counterpart?: {
194
+ publicKey: string;
195
+ publicBoxKey: string;
196
+ userId: string;
197
+ displayName?: string | undefined;
198
+ } | undefined;
199
+ counterpartDeviceId?: string | undefined;
200
+ groupName?: string | undefined;
201
+ token?: {
202
+ type: "group" | "contact";
203
+ nonce: string;
204
+ signature: string;
205
+ expiresAt: string;
206
+ v: 1;
207
+ inviter: {
208
+ publicKey: string;
209
+ publicBoxKey: string;
210
+ userId: string;
211
+ displayName?: string | undefined;
212
+ };
213
+ epoch: number;
214
+ uses: 1 | "unlimited";
215
+ group?: {
216
+ name: string;
217
+ role: GroupMemberRole;
218
+ publicKey: string;
219
+ groupId: string;
220
+ publicBoxKey: string;
221
+ mode: "open" | "approve";
222
+ } | undefined;
223
+ } | undefined;
224
+ payload?: {
225
+ nonce: string;
226
+ contents: string;
227
+ fromPublicKey: string;
228
+ } | undefined;
229
+ parentInviteId?: string | undefined;
230
+ deliveryState?: "queued" | "sent" | undefined;
231
+ recipientUserId?: string | undefined;
232
+ resolvedAt?: Date | undefined;
233
+ note?: string | undefined;
234
+ }, {
235
+ type: "group" | "contact";
236
+ direction: "in" | "out";
237
+ expiresAt: Date;
238
+ mode: "direct" | "open" | "approve" | "contact" | "profile";
239
+ epoch: number;
240
+ inviteId: string;
241
+ role?: GroupMemberRole | undefined;
242
+ nonce?: string | undefined;
243
+ status?: "pending" | "expired" | "accepted" | "declined" | "revoked" | undefined;
244
+ groupId?: string | undefined;
245
+ createdAt?: Date | undefined;
246
+ counterpart?: {
247
+ publicKey: string;
248
+ publicBoxKey: string;
249
+ userId: string;
250
+ displayName?: string | undefined;
251
+ } | undefined;
252
+ counterpartDeviceId?: string | undefined;
253
+ groupName?: string | undefined;
254
+ token?: {
255
+ type: "group" | "contact";
256
+ nonce: string;
257
+ signature: string;
258
+ expiresAt: string;
259
+ v: 1;
260
+ inviter: {
261
+ publicKey: string;
262
+ publicBoxKey: string;
263
+ userId: string;
264
+ displayName?: string | undefined;
265
+ };
266
+ epoch: number;
267
+ uses: 1 | "unlimited";
268
+ group?: {
269
+ name: string;
270
+ role: GroupMemberRole;
271
+ publicKey: string;
272
+ groupId: string;
273
+ publicBoxKey: string;
274
+ mode: "open" | "approve";
275
+ } | undefined;
276
+ } | undefined;
277
+ payload?: {
278
+ nonce: string;
279
+ contents: string;
280
+ fromPublicKey: string;
281
+ } | undefined;
282
+ parentInviteId?: string | undefined;
283
+ deliveryState?: "queued" | "sent" | undefined;
284
+ recipientUserId?: string | undefined;
285
+ resolvedAt?: Date | undefined;
286
+ note?: string | undefined;
287
+ }>;
288
+ /** One pending or historical invite. */
289
+ export type IInvite = z.infer<typeof inviteSchema>;
290
+ /**
291
+ * Accessor for the personal-context {@link Invites} table.
292
+ *
293
+ * @param dataContext - Optional data context; defaults to the current singleton.
294
+ */
295
+ export declare function Invites(dataContext?: DataContext): import("..").Table<{
296
+ type: "group" | "contact";
297
+ direction: "in" | "out";
298
+ status: "pending" | "expired" | "accepted" | "declined" | "revoked";
299
+ expiresAt: Date;
300
+ createdAt: Date;
301
+ mode: "direct" | "open" | "approve" | "contact" | "profile";
302
+ epoch: number;
303
+ inviteId: string;
304
+ role?: GroupMemberRole | undefined;
305
+ nonce?: string | undefined;
306
+ groupId?: string | undefined;
307
+ counterpart?: {
308
+ publicKey: string;
309
+ publicBoxKey: string;
310
+ userId: string;
311
+ displayName?: string | undefined;
312
+ } | undefined;
313
+ counterpartDeviceId?: string | undefined;
314
+ groupName?: string | undefined;
315
+ token?: {
316
+ type: "group" | "contact";
317
+ nonce: string;
318
+ signature: string;
319
+ expiresAt: string;
320
+ v: 1;
321
+ inviter: {
322
+ publicKey: string;
323
+ publicBoxKey: string;
324
+ userId: string;
325
+ displayName?: string | undefined;
326
+ };
327
+ epoch: number;
328
+ uses: 1 | "unlimited";
329
+ group?: {
330
+ name: string;
331
+ role: GroupMemberRole;
332
+ publicKey: string;
333
+ groupId: string;
334
+ publicBoxKey: string;
335
+ mode: "open" | "approve";
336
+ } | undefined;
337
+ } | undefined;
338
+ payload?: {
339
+ nonce: string;
340
+ contents: string;
341
+ fromPublicKey: string;
342
+ } | undefined;
343
+ parentInviteId?: string | undefined;
344
+ deliveryState?: "queued" | "sent" | undefined;
345
+ recipientUserId?: string | undefined;
346
+ resolvedAt?: Date | undefined;
347
+ note?: string | undefined;
348
+ }>;
349
+ /** Age after which terminal rows are purged. */
350
+ export declare const INVITE_HISTORY_RETENTION_MS: number;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.INVITE_HISTORY_RETENTION_MS = exports.inviteSchema = exports.inviteDeliveryStateSchema = exports.inviteModeSchema = exports.inviteStatusSchema = void 0;
4
+ exports.Invites = Invites;
5
+ const zod_1 = require("zod");
6
+ const user_context_singleton_1 = require("../context/user-context-singleton");
7
+ const data_box_schema_1 = require("../invites/data-box-schema");
8
+ const invite_token_1 = require("../invites/invite-token");
9
+ const zod_types_1 = require("../types/zod-types");
10
+ const group_member_roles_1 = require("./group-member-roles");
11
+ const table_definitions_system_1 = require("./orm/table-definitions.system");
12
+ const types_1 = require("./orm/types");
13
+ /** Lifecycle of an invite row. */
14
+ exports.inviteStatusSchema = zod_1.z.enum(["pending", "accepted", "declined", "expired", "revoked"]);
15
+ /**
16
+ * How the invite was issued:
17
+ * - `direct`: a known contact was invited to a group (3a); the payload is the approval.
18
+ * - `approve`: group link whose join requests the inviter approves (3b).
19
+ * - `open`: group link any Admin+ member auto-approves (3c).
20
+ * - `profile`: the reusable contact QR on the Account screen.
21
+ * - `contact`: a one-off contact token.
22
+ */
23
+ exports.inviteModeSchema = zod_1.z.enum(["direct", "approve", "open", "profile", "contact"]);
24
+ /** Outbound delivery progress; `queued` rows are retried when connectivity changes. */
25
+ exports.inviteDeliveryStateSchema = zod_1.z.enum(["queued", "sent"]);
26
+ /**
27
+ * One pending or historical invite, synced across the user's devices.
28
+ *
29
+ * Boxed payloads are encrypted to the user's box key, which every one of their devices
30
+ * holds, so storing them in this personal-context table is safe. Rows move to a terminal
31
+ * status rather than being deleted; terminal rows older than 30 days are purged.
32
+ */
33
+ exports.inviteSchema = zod_1.z.object({
34
+ inviteId: zod_types_1.zodPeerId,
35
+ direction: zod_1.z.enum(["in", "out"]).describe("Whether I issued (out) or received (in) this item"),
36
+ type: zod_1.z.enum(["contact", "group"]),
37
+ status: exports.inviteStatusSchema.default("pending"),
38
+ mode: exports.inviteModeSchema,
39
+ /** The other party, once known (unknown for outstanding link invites). */
40
+ counterpart: invite_token_1.inviteIdentitySchema.optional(),
41
+ /** Device the counterpart used, when they told us; lets the network layer connect early. */
42
+ counterpartDeviceId: zod_types_1.zodPeerId.optional(),
43
+ /**
44
+ * Correlates replies with this row: the token nonce for link invites, a fresh nonce for
45
+ * direct invites.
46
+ */
47
+ nonce: zod_1.z
48
+ .string()
49
+ .regex(/^[A-Za-z0-9_-]+$/)
50
+ .length(22)
51
+ .optional(),
52
+ groupId: zod_types_1.zodPeerId.optional(),
53
+ groupName: zod_1.z.string().optional(),
54
+ role: zod_1.z.nativeEnum(group_member_roles_1.GroupMemberRole).optional().describe("Granted role for group invites"),
55
+ /** The signed token: what I issued (out) or what I received/used (in). */
56
+ token: invite_token_1.peersInviteSchema.optional(),
57
+ /**
58
+ * For `in` direct invites: the GroupApproval boxed to my own user box key. For rows whose
59
+ * `deliveryState` is `queued`: the outbound box (already sealed to `recipientUserId`).
60
+ */
61
+ payload: data_box_schema_1.dataBoxSchema.optional(),
62
+ /** Inviter's epoch at issue time. */
63
+ epoch: zod_1.z.number().int().min(0),
64
+ /** For `in` rows spawned by one of my reusable `out` tokens. */
65
+ parentInviteId: zod_types_1.zodPeerId.optional(),
66
+ deliveryState: exports.inviteDeliveryStateSchema.optional(),
67
+ /** Outbound recipient for `direct` invites and replies that are still queued. */
68
+ recipientUserId: zod_types_1.zodPeerId.optional(),
69
+ createdAt: zod_1.z.date().default(() => new Date()),
70
+ expiresAt: zod_1.z.date(),
71
+ resolvedAt: zod_1.z.date().optional(),
72
+ note: zod_1.z.string().max(500).optional().describe("Sanitized status text for the UI"),
73
+ });
74
+ const metaData = {
75
+ name: "Invites",
76
+ description: "Pending and historical contact and group invites, synced across my devices.",
77
+ primaryKeyName: "inviteId",
78
+ fields: (0, types_1.schemaToFields)(exports.inviteSchema),
79
+ iconClassName: "bi bi-envelope-paper",
80
+ indexes: [
81
+ { fields: ["status"] },
82
+ { fields: ["direction"] },
83
+ { fields: ["groupId"] },
84
+ { fields: ["parentInviteId"] },
85
+ { fields: ["nonce"] },
86
+ { fields: ["deliveryState"] },
87
+ ],
88
+ };
89
+ (0, table_definitions_system_1.registerSystemTableDefinition)(metaData, exports.inviteSchema);
90
+ /**
91
+ * Accessor for the personal-context {@link Invites} table.
92
+ *
93
+ * @param dataContext - Optional data context; defaults to the current singleton.
94
+ */
95
+ function Invites(dataContext) {
96
+ return (0, user_context_singleton_1.getTableContainer)(dataContext).getTable(metaData, exports.inviteSchema);
97
+ }
98
+ /** Age after which terminal rows are purged. */
99
+ exports.INVITE_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
@@ -93,6 +93,10 @@ let UsersTable = (() => {
93
93
  }
94
94
  // Get the old user record if it exists
95
95
  const oldUser = await this.get(user.userId);
96
+ if (opts.weakInsert && oldUser?.signature && !user.signature) {
97
+ // A weak (placeholder) write must never clobber a signed record; keep what we have.
98
+ return oldUser;
99
+ }
96
100
  await (0, user_permissions_1.verifyUserSignature)(user, oldUser);
97
101
  return super.save(user, opts);
98
102
  }
@@ -1,7 +1,15 @@
1
- /** Wire-protocol version for the temporary device-pairing ceremony. */
2
- export declare const DEVICE_PAIRING_PROTOCOL_VERSION: 1;
3
- /** Maximum lifetime of a pairing invitation and signaling room. */
1
+ /**
2
+ * Wire-protocol version for the temporary device-pairing ceremony.
3
+ *
4
+ * - v2: reversed roles + PAKE.
5
+ * - v3: negotiated data transport (WebRTC or direct WebSocket); endpoint nonces move from the
6
+ * SDP descriptions into the transport messages and the transcript binds `transportBinding`.
7
+ */
8
+ export declare const DEVICE_PAIRING_PROTOCOL_VERSION: 3;
9
+ /** Maximum lifetime of a pairing code and its signaling room. */
4
10
  export declare const DEVICE_PAIRING_TTL_MS: number;
11
+ /** Largest nameplate the service allocates; codes read `<1..9999>-<word>-<word>-<word>`. */
12
+ export declare const DEVICE_PAIRING_MAX_NAMEPLATE = 9999;
5
13
  /** Socket.IO namespace reserved for device-pairing signaling. */
6
14
  export declare const DEVICE_PAIRING_SIGNALING_NAMESPACE = "/device-pairing-v1";
7
15
  /** Dedicated Socket.IO engine path with a pairing-sized transport buffer. */
@@ -22,13 +30,23 @@ export declare const DEVICE_PAIRING_MAX_SIGNAL_BYTES: number;
22
30
  export declare const DEVICE_PAIRING_MAX_TRANSPORT_BYTES: number;
23
31
  /** Maximum ICE candidates accepted from either endpoint. */
24
32
  export declare const DEVICE_PAIRING_MAX_ICE_CANDIDATES_PER_ROLE = 64;
33
+ /** Maximum direct WebSocket URLs a destination may advertise in its transport options. */
34
+ export declare const DEVICE_PAIRING_MAX_WEBSOCKET_URLS = 8;
35
+ /**
36
+ * Socket.IO namespace a destination hosts for the direct WebSocket pairing transport. The
37
+ * source authenticates with the PAKE-derived WebSocket token in `auth.token`.
38
+ */
39
+ export declare const DEVICE_PAIRING_WEBSOCKET_NAMESPACE = "/device-pairing-transport-v1";
25
40
  /** Maximum tolerated source/destination wall-clock difference. */
26
41
  export declare const DEVICE_PAIRING_CLOCK_SKEW_MS: number;
27
42
  /** Event name used to publish renderer-safe pairing state. */
28
43
  export declare const DEVICE_PAIRING_STATE_EVENT = "device-pairing:state";
29
- /** Domain-separation label for the room bearer credential. */
30
- export declare const DEVICE_PAIRING_ROOM_ACCESS_LABEL = "peers-pairing-room-access-v1";
31
- /** Domain-separation label for signaling message authentication. */
32
- export declare const DEVICE_PAIRING_SIGNALING_LABEL = "peers-pairing-signaling-v1";
33
- /** Domain-separation label for the six-digit short authentication string. */
34
- export declare const DEVICE_PAIRING_SAS_LABEL = "peers-pairing-sas-v1";
44
+ /** Domain-separation label for the PAKE-derived signaling authentication key. */
45
+ export declare const DEVICE_PAIRING_SIGNALING_LABEL = "peers-pairing-signaling-v3";
46
+ /** Domain-separation label for the PAKE-derived transcript confirmation key. */
47
+ export declare const DEVICE_PAIRING_CONFIRM_LABEL = "peers-pairing-confirm-v3";
48
+ /**
49
+ * Domain-separation label for the token a source presents when it connects to a destination's
50
+ * direct WebSocket pairing listener. Derived from the signaling key; never sent via the relay.
51
+ */
52
+ export declare const DEVICE_PAIRING_WEBSOCKET_TOKEN_LABEL = "peers-pairing-websocket-token-v3";
@@ -1,10 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEVICE_PAIRING_SAS_LABEL = exports.DEVICE_PAIRING_SIGNALING_LABEL = exports.DEVICE_PAIRING_ROOM_ACCESS_LABEL = exports.DEVICE_PAIRING_STATE_EVENT = exports.DEVICE_PAIRING_CLOCK_SKEW_MS = exports.DEVICE_PAIRING_MAX_ICE_CANDIDATES_PER_ROLE = exports.DEVICE_PAIRING_MAX_TRANSPORT_BYTES = exports.DEVICE_PAIRING_MAX_SIGNAL_BYTES = exports.DEVICE_PAIRING_SIGNALING_EVENTS = exports.DEVICE_PAIRING_SIGNALING_PATH = exports.DEVICE_PAIRING_SIGNALING_NAMESPACE = exports.DEVICE_PAIRING_TTL_MS = exports.DEVICE_PAIRING_PROTOCOL_VERSION = void 0;
4
- /** Wire-protocol version for the temporary device-pairing ceremony. */
5
- exports.DEVICE_PAIRING_PROTOCOL_VERSION = 1;
6
- /** Maximum lifetime of a pairing invitation and signaling room. */
3
+ exports.DEVICE_PAIRING_WEBSOCKET_TOKEN_LABEL = exports.DEVICE_PAIRING_CONFIRM_LABEL = exports.DEVICE_PAIRING_SIGNALING_LABEL = exports.DEVICE_PAIRING_STATE_EVENT = exports.DEVICE_PAIRING_CLOCK_SKEW_MS = exports.DEVICE_PAIRING_WEBSOCKET_NAMESPACE = exports.DEVICE_PAIRING_MAX_WEBSOCKET_URLS = exports.DEVICE_PAIRING_MAX_ICE_CANDIDATES_PER_ROLE = exports.DEVICE_PAIRING_MAX_TRANSPORT_BYTES = exports.DEVICE_PAIRING_MAX_SIGNAL_BYTES = exports.DEVICE_PAIRING_SIGNALING_EVENTS = exports.DEVICE_PAIRING_SIGNALING_PATH = exports.DEVICE_PAIRING_SIGNALING_NAMESPACE = exports.DEVICE_PAIRING_MAX_NAMEPLATE = exports.DEVICE_PAIRING_TTL_MS = exports.DEVICE_PAIRING_PROTOCOL_VERSION = void 0;
4
+ /**
5
+ * Wire-protocol version for the temporary device-pairing ceremony.
6
+ *
7
+ * - v2: reversed roles + PAKE.
8
+ * - v3: negotiated data transport (WebRTC or direct WebSocket); endpoint nonces move from the
9
+ * SDP descriptions into the transport messages and the transcript binds `transportBinding`.
10
+ */
11
+ exports.DEVICE_PAIRING_PROTOCOL_VERSION = 3;
12
+ /** Maximum lifetime of a pairing code and its signaling room. */
7
13
  exports.DEVICE_PAIRING_TTL_MS = 5 * 60 * 1000;
14
+ /** Largest nameplate the service allocates; codes read `<1..9999>-<word>-<word>-<word>`. */
15
+ exports.DEVICE_PAIRING_MAX_NAMEPLATE = 9999;
8
16
  /** Socket.IO namespace reserved for device-pairing signaling. */
9
17
  exports.DEVICE_PAIRING_SIGNALING_NAMESPACE = "/device-pairing-v1";
10
18
  /** Dedicated Socket.IO engine path with a pairing-sized transport buffer. */
@@ -25,13 +33,23 @@ exports.DEVICE_PAIRING_MAX_SIGNAL_BYTES = 72 * 1024;
25
33
  exports.DEVICE_PAIRING_MAX_TRANSPORT_BYTES = 96 * 1024;
26
34
  /** Maximum ICE candidates accepted from either endpoint. */
27
35
  exports.DEVICE_PAIRING_MAX_ICE_CANDIDATES_PER_ROLE = 64;
36
+ /** Maximum direct WebSocket URLs a destination may advertise in its transport options. */
37
+ exports.DEVICE_PAIRING_MAX_WEBSOCKET_URLS = 8;
38
+ /**
39
+ * Socket.IO namespace a destination hosts for the direct WebSocket pairing transport. The
40
+ * source authenticates with the PAKE-derived WebSocket token in `auth.token`.
41
+ */
42
+ exports.DEVICE_PAIRING_WEBSOCKET_NAMESPACE = "/device-pairing-transport-v1";
28
43
  /** Maximum tolerated source/destination wall-clock difference. */
29
44
  exports.DEVICE_PAIRING_CLOCK_SKEW_MS = 2 * 60 * 1000;
30
45
  /** Event name used to publish renderer-safe pairing state. */
31
46
  exports.DEVICE_PAIRING_STATE_EVENT = "device-pairing:state";
32
- /** Domain-separation label for the room bearer credential. */
33
- exports.DEVICE_PAIRING_ROOM_ACCESS_LABEL = "peers-pairing-room-access-v1";
34
- /** Domain-separation label for signaling message authentication. */
35
- exports.DEVICE_PAIRING_SIGNALING_LABEL = "peers-pairing-signaling-v1";
36
- /** Domain-separation label for the six-digit short authentication string. */
37
- exports.DEVICE_PAIRING_SAS_LABEL = "peers-pairing-sas-v1";
47
+ /** Domain-separation label for the PAKE-derived signaling authentication key. */
48
+ exports.DEVICE_PAIRING_SIGNALING_LABEL = "peers-pairing-signaling-v3";
49
+ /** Domain-separation label for the PAKE-derived transcript confirmation key. */
50
+ exports.DEVICE_PAIRING_CONFIRM_LABEL = "peers-pairing-confirm-v3";
51
+ /**
52
+ * Domain-separation label for the token a source presents when it connects to a destination's
53
+ * direct WebSocket pairing listener. Derived from the signaling key; never sent via the relay.
54
+ */
55
+ exports.DEVICE_PAIRING_WEBSOCKET_TOKEN_LABEL = "peers-pairing-websocket-token-v3";
@@ -0,0 +1,46 @@
1
+ /** Role in the one-round CPace exchange; the initiator's share is listed first in the transcript. */
2
+ export type CpaceRole = "initiator" | "responder";
3
+ /** Inputs that both parties must agree on for the exchange to succeed. */
4
+ export interface CpaceParameters {
5
+ /** Password-related string (PRS); for pairing, the code words. */
6
+ password: Uint8Array;
7
+ /** Channel identifier (CI) binding the exchange to one rendezvous. */
8
+ channelId: Uint8Array;
9
+ /** Optional session id (sid); empty when the channel identifier is already unique. */
10
+ sessionId?: Uint8Array;
11
+ /** Associated data sent alongside the initiator's share. */
12
+ initiatorAd?: Uint8Array;
13
+ /** Associated data sent alongside the responder's share. */
14
+ responderAd?: Uint8Array;
15
+ }
16
+ /** One party's in-progress exchange. */
17
+ export interface CpaceSession {
18
+ /** Public share (32-byte X25519 u-coordinate) to send to the other party. */
19
+ readonly share: Uint8Array;
20
+ /**
21
+ * Consumes the remote share and returns the 64-byte ISK.
22
+ * @throws when the remote share encodes a low-order point (the exchange must be aborted).
23
+ */
24
+ finish(remoteShare: Uint8Array): Uint8Array;
25
+ }
26
+ /** LEB128 length prefix as defined by the draft's `prepend_len`. */
27
+ export declare function cpacePrependLength(data: Uint8Array): Uint8Array;
28
+ /** Length-prefixed concatenation (`lv_cat`). */
29
+ export declare function cpaceLvCat(...parts: Uint8Array[]): Uint8Array;
30
+ /** Builds the `generator_string` that is hashed into the secret generator. */
31
+ export declare function cpaceGeneratorString(password: Uint8Array, channelId: Uint8Array, sessionId: Uint8Array): Uint8Array;
32
+ /** Elligator 2 map from a field element to a Curve25519 u-coordinate (RFC 9380 `map_to_curve_elligator2`). */
33
+ export declare function cpaceElligator2(u: bigint): Uint8Array;
34
+ /** Derives the password-dependent generator `g` for one exchange. */
35
+ export declare function cpaceCalculateGenerator(password: Uint8Array, channelId: Uint8Array, sessionId?: Uint8Array): Uint8Array;
36
+ /** X25519 scalar multiplication that rejects results in the small subgroup (`scalar_mult_vfy`). */
37
+ export declare function cpaceScalarMultVerify(scalar: Uint8Array, point: Uint8Array): Uint8Array;
38
+ /** Initiator/responder transcript (`transcript_ir`). */
39
+ export declare function cpaceTranscript(initiatorShare: Uint8Array, initiatorAd: Uint8Array, responderShare: Uint8Array, responderAd: Uint8Array): Uint8Array;
40
+ /** Final ISK derivation: `H(lv_cat(DSI_ISK, sid, K) || transcript)`. */
41
+ export declare function cpaceDeriveIsk(sharedPoint: Uint8Array, sessionId: Uint8Array, transcript: Uint8Array): Uint8Array;
42
+ /**
43
+ * Starts one side of a CPace exchange. The returned share is safe to send in the clear.
44
+ * `scalar` may be injected for deterministic test vectors; production callers omit it.
45
+ */
46
+ export declare function cpaceStart(role: CpaceRole, parameters: CpaceParameters, scalar?: Uint8Array): CpaceSession;