@agora-sdk/secure-chat-core 0.4.0 → 0.5.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 (55) hide show
  1. package/dist/cjs/backup/passphrase-strength.d.ts +22 -0
  2. package/dist/cjs/backup/passphrase-strength.js +75 -0
  3. package/dist/cjs/backup/passphrase-strength.js.map +1 -0
  4. package/dist/cjs/context/secure-chat-context.d.ts +12 -1
  5. package/dist/cjs/context/secure-chat-context.js +3 -1
  6. package/dist/cjs/context/secure-chat-context.js.map +1 -1
  7. package/dist/cjs/hooks/useSecureBackup.d.ts +67 -0
  8. package/dist/cjs/hooks/useSecureBackup.js +207 -0
  9. package/dist/cjs/hooks/useSecureBackup.js.map +1 -0
  10. package/dist/cjs/hooks/useSecureDevice.d.ts +21 -1
  11. package/dist/cjs/hooks/useSecureDevice.js +46 -5
  12. package/dist/cjs/hooks/useSecureDevice.js.map +1 -1
  13. package/dist/cjs/hooks/useSecureMessages.d.ts +16 -3
  14. package/dist/cjs/hooks/useSecureMessages.js +38 -15
  15. package/dist/cjs/hooks/useSecureMessages.js.map +1 -1
  16. package/dist/cjs/hooks/useSecureSafetyNumber.d.ts +30 -0
  17. package/dist/cjs/hooks/useSecureSafetyNumber.js +82 -0
  18. package/dist/cjs/hooks/useSecureSafetyNumber.js.map +1 -0
  19. package/dist/cjs/index.d.ts +12 -1
  20. package/dist/cjs/index.js +16 -1
  21. package/dist/cjs/index.js.map +1 -1
  22. package/dist/cjs/util/padding.d.ts +39 -0
  23. package/dist/cjs/util/padding.js +80 -0
  24. package/dist/cjs/util/padding.js.map +1 -0
  25. package/dist/cjs/util/safety-number.d.ts +27 -0
  26. package/dist/cjs/util/safety-number.js +84 -0
  27. package/dist/cjs/util/safety-number.js.map +1 -0
  28. package/dist/esm/backup/passphrase-strength.d.ts +22 -0
  29. package/dist/esm/backup/passphrase-strength.js +72 -0
  30. package/dist/esm/backup/passphrase-strength.js.map +1 -0
  31. package/dist/esm/context/secure-chat-context.d.ts +12 -1
  32. package/dist/esm/context/secure-chat-context.js +3 -1
  33. package/dist/esm/context/secure-chat-context.js.map +1 -1
  34. package/dist/esm/hooks/useSecureBackup.d.ts +67 -0
  35. package/dist/esm/hooks/useSecureBackup.js +204 -0
  36. package/dist/esm/hooks/useSecureBackup.js.map +1 -0
  37. package/dist/esm/hooks/useSecureDevice.d.ts +21 -1
  38. package/dist/esm/hooks/useSecureDevice.js +46 -5
  39. package/dist/esm/hooks/useSecureDevice.js.map +1 -1
  40. package/dist/esm/hooks/useSecureMessages.d.ts +16 -3
  41. package/dist/esm/hooks/useSecureMessages.js +38 -15
  42. package/dist/esm/hooks/useSecureMessages.js.map +1 -1
  43. package/dist/esm/hooks/useSecureSafetyNumber.d.ts +30 -0
  44. package/dist/esm/hooks/useSecureSafetyNumber.js +79 -0
  45. package/dist/esm/hooks/useSecureSafetyNumber.js.map +1 -0
  46. package/dist/esm/index.d.ts +12 -1
  47. package/dist/esm/index.js +7 -0
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/util/padding.d.ts +39 -0
  50. package/dist/esm/util/padding.js +75 -0
  51. package/dist/esm/util/padding.js.map +1 -0
  52. package/dist/esm/util/safety-number.d.ts +27 -0
  53. package/dist/esm/util/safety-number.js +81 -0
  54. package/dist/esm/util/safety-number.js.map +1 -0
  55. package/package.json +2 -2
@@ -0,0 +1,22 @@
1
+ /** The outcome of {@link estimatePassphraseStrength}: a coarse 0–4 score plus display copy. */
2
+ export interface PassphraseStrength {
3
+ /** 0 (weakest) … 4 (strongest) — for a 5-segment meter. */
4
+ score: 0 | 1 | 2 | 3 | 4;
5
+ /** A short human label for the score (e.g. "Weak", "Strong"). */
6
+ label: string;
7
+ /** A specific reason the passphrase is weak, when applicable (e.g. a common password). */
8
+ warning?: string;
9
+ }
10
+ /**
11
+ * Estimate the strength of a backup passphrase for a strength meter.
12
+ *
13
+ * @param passphrase - The candidate passphrase (never logged or sent anywhere).
14
+ * @returns A {@link PassphraseStrength} with a 0–4 score, a label, and an optional warning.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const { score, label, warning } = estimatePassphraseStrength(input);
19
+ * // render a 5-segment meter from `score`, show `warning` if present.
20
+ * ```
21
+ */
22
+ export declare function estimatePassphraseStrength(passphrase: string): PassphraseStrength;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ // Passphrase-strength estimator for backup UX.
3
+ //
4
+ // Why this exists (CLAUDE.md #1 + spec §16.5): the blind server stores the passphrase-encrypted
5
+ // backup blob, so a weak passphrase is offline-brute-forceable on a DB exfil. The real argon2id KDF
6
+ // raises the cost per guess, but it can't rescue a guessable passphrase — so we nudge the user toward
7
+ // a strong one at entry time. This is a deliberately small, dependency-free heuristic (length +
8
+ // character-class diversity + a common-password penalty), NOT a substitute for zxcvbn-grade entropy
9
+ // estimation. It runs purely client-side and never logs or transmits the passphrase.
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.estimatePassphraseStrength = estimatePassphraseStrength;
12
+ const LABELS = ["Very weak", "Weak", "Fair", "Strong", "Very strong"];
13
+ // A tiny set of the most-guessed passwords/substrings. Not exhaustive — a guardrail against the
14
+ // obvious, not a real dictionary check.
15
+ const COMMON = [
16
+ "password", "passw0rd", "12345", "123456", "qwerty", "letmein", "admin", "welcome",
17
+ "iloveyou", "abc123", "hunter2", "monkey", "dragon", "trustno1", "secret",
18
+ ];
19
+ /**
20
+ * Estimate the strength of a backup passphrase for a strength meter.
21
+ *
22
+ * @param passphrase - The candidate passphrase (never logged or sent anywhere).
23
+ * @returns A {@link PassphraseStrength} with a 0–4 score, a label, and an optional warning.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const { score, label, warning } = estimatePassphraseStrength(input);
28
+ * // render a 5-segment meter from `score`, show `warning` if present.
29
+ * ```
30
+ */
31
+ function estimatePassphraseStrength(passphrase) {
32
+ const pw = passphrase ?? "";
33
+ if (pw.length === 0)
34
+ return { score: 0, label: LABELS[0] };
35
+ const lower = pw.toLowerCase();
36
+ const hitsCommon = COMMON.some((c) => lower.includes(c));
37
+ // Character-class diversity.
38
+ let classes = 0;
39
+ if (/[a-z]/.test(pw))
40
+ classes++;
41
+ if (/[A-Z]/.test(pw))
42
+ classes++;
43
+ if (/[0-9]/.test(pw))
44
+ classes++;
45
+ if (/[^a-zA-Z0-9]/.test(pw))
46
+ classes++;
47
+ // Length is the dominant factor (passphrases >> complex-but-short passwords).
48
+ let score = 0;
49
+ if (pw.length >= 8)
50
+ score++;
51
+ if (pw.length >= 12)
52
+ score++;
53
+ if (pw.length >= 16)
54
+ score++;
55
+ // Diversity adds at most one step, and only once there's some length.
56
+ if (classes >= 3 && pw.length >= 8)
57
+ score++;
58
+ // A short, single-class passphrase never rates above "weak".
59
+ if (pw.length < 8 || classes <= 1)
60
+ score = Math.min(score, 1);
61
+ // A recognizably common password is capped hard regardless of shape.
62
+ if (hitsCommon)
63
+ score = Math.min(score, 1);
64
+ const clamped = Math.max(0, Math.min(4, score));
65
+ return {
66
+ score: clamped,
67
+ label: LABELS[clamped],
68
+ warning: hitsCommon
69
+ ? "This looks like a common password — choose something less guessable."
70
+ : clamped <= 1
71
+ ? "Use a longer passphrase (4+ random words) with mixed character types."
72
+ : undefined,
73
+ };
74
+ }
75
+ //# sourceMappingURL=passphrase-strength.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passphrase-strength.js","sourceRoot":"","sources":["../../../src/backup/passphrase-strength.ts"],"names":[],"mappings":";AAAA,+CAA+C;AAC/C,EAAE;AACF,gGAAgG;AAChG,oGAAoG;AACpG,sGAAsG;AACtG,gGAAgG;AAChG,oGAAoG;AACpG,qFAAqF;;AAiCrF,gEAsCC;AA3DD,MAAM,MAAM,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAU,CAAC;AAE/E,gGAAgG;AAChG,wCAAwC;AACxC,MAAM,MAAM,GAAG;IACb,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS;IAClF,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ;CAC1E,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,SAAgB,0BAA0B,CAAC,UAAkB;IAC3D,MAAM,EAAE,GAAG,UAAU,IAAI,EAAE,CAAC;IAC5B,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3D,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzD,6BAA6B;IAC7B,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,8EAA8E;IAC9E,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC;QAAE,KAAK,EAAE,CAAC;IAC5B,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE;QAAE,KAAK,EAAE,CAAC;IAC7B,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE;QAAE,KAAK,EAAE,CAAC;IAC7B,sEAAsE;IACtE,IAAI,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC;QAAE,KAAK,EAAE,CAAC;IAE5C,6DAA6D;IAC7D,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,IAAI,CAAC;QAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAE9D,qEAAqE;IACrE,IAAI,UAAU;QAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAE3C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAgC,CAAC;IAC/E,OAAO;QACL,KAAK,EAAE,OAAO;QACd,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC;QACtB,OAAO,EAAE,UAAU;YACjB,CAAC,CAAC,sEAAsE;YACxE,CAAC,CAAC,OAAO,IAAI,CAAC;gBACZ,CAAC,CAAC,uEAAuE;gBACzE,CAAC,CAAC,SAAS;KAChB,CAAC;AACJ,CAAC"}
@@ -4,6 +4,7 @@ import { SecureChatRestClient } from "../transport/rest.js";
4
4
  import { SecureChatSocketClient } from "../transport/socket.js";
5
5
  import { SecureChatStore } from "../persistence/store.js";
6
6
  import { SecureChatRepository } from "../persistence/repository.js";
7
+ import { PaddingPolicy } from "../util/padding.js";
7
8
  /**
8
9
  * The value exposed by {@link useSecureChat}: shared transport clients, the injected crypto, the
9
10
  * persistence repository, the group-handle resolver, and the active project id.
@@ -31,6 +32,11 @@ export interface SecureChatContextValue {
31
32
  * @returns An unsubscribe function.
32
33
  */
33
34
  subscribeGroupChange: (listener: () => void) => () => void;
35
+ /**
36
+ * Outbound message size-bucket padding policy. `useSecureMessages` pads plaintext to this before
37
+ * encryption so ciphertext size leaks less; defaults to `"ladder"`.
38
+ */
39
+ padding: PaddingPolicy;
34
40
  /** The Agora project id these clients are scoped to. */
35
41
  projectId: string;
36
42
  }
@@ -50,6 +56,11 @@ export interface SecureChatProviderProps {
50
56
  baseUrl?: string;
51
57
  /** Override the socket origin. Defaults to @agora-sdk/core `getSocketUrl()`. */
52
58
  socketUrl?: string;
59
+ /**
60
+ * Outbound message size-bucket padding policy (metadata hardening). `"ladder"` (default) pads each
61
+ * message up to a fixed size bucket so ciphertext length leaks less; `"none"` frames without padding.
62
+ */
63
+ padding?: PaddingPolicy;
53
64
  children: React.ReactNode;
54
65
  }
55
66
  /**
@@ -66,7 +77,7 @@ export interface SecureChatProviderProps {
66
77
  * </SecureChatProvider>
67
78
  * ```
68
79
  */
69
- export declare function SecureChatProvider({ crypto, projectId, store, accessToken, getAccessToken, baseUrl, socketUrl, children, }: SecureChatProviderProps): React.JSX.Element;
80
+ export declare function SecureChatProvider({ crypto, projectId, store, accessToken, getAccessToken, baseUrl, socketUrl, padding, children, }: SecureChatProviderProps): React.JSX.Element;
70
81
  /**
71
82
  * Access the nearest {@link SecureChatContextValue}.
72
83
  *
@@ -31,7 +31,7 @@ const SecureChatContext = (0, react_1.createContext)(null);
31
31
  * </SecureChatProvider>
32
32
  * ```
33
33
  */
34
- function SecureChatProvider({ crypto, projectId, store, accessToken, getAccessToken, baseUrl, socketUrl, children, }) {
34
+ function SecureChatProvider({ crypto, projectId, store, accessToken, getAccessToken, baseUrl, socketUrl, padding = "ladder", children, }) {
35
35
  const tokenRef = (0, react_1.useRef)(accessToken);
36
36
  tokenRef.current = accessToken;
37
37
  const resolveToken = (0, react_1.useMemo)(() => getAccessToken ?? (() => tokenRef.current), [getAccessToken]);
@@ -95,6 +95,7 @@ function SecureChatProvider({ crypto, projectId, store, accessToken, getAccessTo
95
95
  rememberGroup,
96
96
  getGroupVersion,
97
97
  subscribeGroupChange,
98
+ padding,
98
99
  projectId,
99
100
  }), [
100
101
  rest,
@@ -105,6 +106,7 @@ function SecureChatProvider({ crypto, projectId, store, accessToken, getAccessTo
105
106
  rememberGroup,
106
107
  getGroupVersion,
107
108
  subscribeGroupChange,
109
+ padding,
108
110
  projectId,
109
111
  ]);
110
112
  return (0, jsx_runtime_1.jsx)(SecureChatContext.Provider, { value: value, children: children });
@@ -1 +1 @@
1
- {"version":3,"file":"secure-chat-context.js","sourceRoot":"","sources":["../../../src/context/secure-chat-context.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"secure-chat-context.js","sourceRoot":"","sources":["../../../src/context/secure-chat-context.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { type PassphraseStrength } from "../backup/passphrase-strength.js";
2
+ /** State + actions returned by {@link useSecureBackup}. */
3
+ export interface UseSecureBackupValues {
4
+ /**
5
+ * Seal all local key material under `passphrase` and upload it to the blind server.
6
+ * @throws {Error} If export or upload fails.
7
+ */
8
+ backup: (passphrase: string) => Promise<void>;
9
+ /**
10
+ * Restore local key material on a fresh client from the server's backup, rehydrating the device and
11
+ * every conversation's group state.
12
+ * @throws {Error} If no backup exists, the passphrase is wrong, or the backup is corrupt.
13
+ */
14
+ restore: (passphrase: string) => Promise<void>;
15
+ /** True while {@link UseSecureBackupValues.backup} is in flight. */
16
+ backingUp: boolean;
17
+ /** True while {@link UseSecureBackupValues.restore} is in flight. */
18
+ restoring: boolean;
19
+ /** The last error thrown by backup or restore, or `null`. */
20
+ error: unknown;
21
+ /** Server `updatedAt` of the most recent successful upload this session, or `null`. */
22
+ lastBackupAt: string | null;
23
+ /** True once a group has advanced since the last backup (membership/epoch change) — prompt a re-backup. */
24
+ needsBackup: boolean;
25
+ /**
26
+ * True when there is NO local key material but a backup exists on the server — i.e. this client was
27
+ * evicted (Safari ITP / "clear browsing data") or is a fresh browser, and recovery is possible. The
28
+ * app should route to a passphrase prompt → {@link UseSecureBackupValues.restore} instead of
29
+ * registering a new device. Indistinguishable cases (evicted vs cleared vs new browser) all resolve
30
+ * the same way. Cleared after a successful restore.
31
+ */
32
+ needsRestore: boolean;
33
+ /** True while the mount-time eviction check (or {@link UseSecureBackupValues.recheckRestore}) is in flight. */
34
+ checkingRestore: boolean;
35
+ /**
36
+ * Re-run the eviction check on demand (e.g. after catching a storage error mid-session, or after
37
+ * sign-in). Skips the server entirely when local key material is present.
38
+ * @returns The new {@link UseSecureBackupValues.needsRestore} value.
39
+ */
40
+ recheckRestore: () => Promise<boolean>;
41
+ /** Coarse client-side passphrase-strength estimate for a meter (see {@link estimatePassphraseStrength}). */
42
+ estimateStrength: (passphrase: string) => PassphraseStrength;
43
+ }
44
+ /**
45
+ * Manage passphrase backup + restore of the client's secure-chat key material.
46
+ *
47
+ * Backups are explicit (call {@link UseSecureBackupValues.backup}); `needsBackup` flips true when a
48
+ * group advances so the app can prompt. On mount it also detects an evicted/fresh client
49
+ * (`needsRestore`) so the app restores rather than registering a fresh, history-less identity.
50
+ *
51
+ * @returns Backup/restore actions, the evicted-client `needsRestore` signal, in-flight + error state, a
52
+ * stale-backup signal, and a passphrase-strength helper.
53
+ * @throws {Error} When used outside a `<SecureChatProvider>`.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * const { needsRestore, restore, backup } = useSecureBackup();
58
+ * const { device, register } = useSecureDevice();
59
+ * // Route an evicted / fresh client to restore; a true first-run to register.
60
+ * if (needsRestore) await restore(passphrase); // prompt for the passphrase first
61
+ * else if (!device) await register();
62
+ * await backup(passphrase); // later, after a device/chat exists
63
+ * ```
64
+ * Restore should complete before the app relies on `useSecureDevice` (which read `device: null` on its
65
+ * own mount) — gate the chat subtree on a post-restore flag or remount it after restore.
66
+ */
67
+ export declare function useSecureBackup(): UseSecureBackupValues;
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ // useSecureBackup — passphrase backup + restore of all local key material.
3
+ //
4
+ // Backup: crypto.exportBackup(passphrase) seals the device identity + every group's state under a
5
+ // real argon2id KDF + AEAD; we base64 the blob/nonce at the wire boundary and PUT it to the blind
6
+ // server, which stores the ciphertext verbatim (it never sees the passphrase or plaintext).
7
+ //
8
+ // Restore (fresh browser): GET the blob → crypto.importBackup re-derives the identity + groups in
9
+ // memory → re-assert the device server-side (idempotent) to recover its row → persist the device →
10
+ // rebind each conversation's group state by conversationId from the server's conversation list (the
11
+ // server is the source of truth for membership; the backup itself is conversationId-agnostic). The
12
+ // handshake cursor is intentionally left unset so useSecureHandshakes re-pulls from since=0 and
13
+ // dedupes — no cursor needs to live in the backup.
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.useSecureBackup = useSecureBackup;
16
+ const react_1 = require("react");
17
+ const base64_js_1 = require("../util/base64.js");
18
+ const secure_chat_context_js_1 = require("../context/secure-chat-context.js");
19
+ const passphrase_strength_js_1 = require("../backup/passphrase-strength.js");
20
+ /**
21
+ * Manage passphrase backup + restore of the client's secure-chat key material.
22
+ *
23
+ * Backups are explicit (call {@link UseSecureBackupValues.backup}); `needsBackup` flips true when a
24
+ * group advances so the app can prompt. On mount it also detects an evicted/fresh client
25
+ * (`needsRestore`) so the app restores rather than registering a fresh, history-less identity.
26
+ *
27
+ * @returns Backup/restore actions, the evicted-client `needsRestore` signal, in-flight + error state, a
28
+ * stale-backup signal, and a passphrase-strength helper.
29
+ * @throws {Error} When used outside a `<SecureChatProvider>`.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * const { needsRestore, restore, backup } = useSecureBackup();
34
+ * const { device, register } = useSecureDevice();
35
+ * // Route an evicted / fresh client to restore; a true first-run to register.
36
+ * if (needsRestore) await restore(passphrase); // prompt for the passphrase first
37
+ * else if (!device) await register();
38
+ * await backup(passphrase); // later, after a device/chat exists
39
+ * ```
40
+ * Restore should complete before the app relies on `useSecureDevice` (which read `device: null` on its
41
+ * own mount) — gate the chat subtree on a post-restore flag or remount it after restore.
42
+ */
43
+ function useSecureBackup() {
44
+ const { crypto, rest, repo, rememberGroup, subscribeGroupChange } = (0, secure_chat_context_js_1.useSecureChat)();
45
+ const [backingUp, setBackingUp] = (0, react_1.useState)(false);
46
+ const [restoring, setRestoring] = (0, react_1.useState)(false);
47
+ const [error, setError] = (0, react_1.useState)(null);
48
+ const [lastBackupAt, setLastBackupAt] = (0, react_1.useState)(null);
49
+ const [needsBackup, setNeedsBackup] = (0, react_1.useState)(false);
50
+ const [needsRestore, setNeedsRestore] = (0, react_1.useState)(false);
51
+ const [checkingRestore, setCheckingRestore] = (0, react_1.useState)(false);
52
+ // A group advancing (a join or a processed Commit) means the on-server backup is now stale.
53
+ // Guard the very first synchronous fire so mount doesn't immediately flag a backup as needed.
54
+ const armed = (0, react_1.useRef)(false);
55
+ (0, react_1.useEffect)(() => {
56
+ armed.current = true;
57
+ return subscribeGroupChange(() => {
58
+ if (armed.current)
59
+ setNeedsBackup(true);
60
+ });
61
+ }, [subscribeGroupChange]);
62
+ // Eviction / fresh-client detection: local key material gone but a server backup exists ⇒ restore is
63
+ // possible (and preferable to registering a new, history-less identity). Evicted, "cleared browsing
64
+ // data", and a brand-new browser are indistinguishable here and resolve the same way. We only hit the
65
+ // server when there's no local device, so the common (healthy) path stays a single IndexedDB read.
66
+ const recheckRestore = (0, react_1.useCallback)(async () => {
67
+ setCheckingRestore(true);
68
+ try {
69
+ const persisted = await repo.loadDevice();
70
+ if (persisted) {
71
+ setNeedsRestore(false);
72
+ return false;
73
+ }
74
+ const model = await rest.getKeyBackup();
75
+ const possible = model !== null;
76
+ setNeedsRestore(possible);
77
+ return possible;
78
+ }
79
+ catch (err) {
80
+ // Fail soft: a network blip must not strand the user as "needs restore". Surface the error only.
81
+ setError(err);
82
+ setNeedsRestore(false);
83
+ return false;
84
+ }
85
+ finally {
86
+ setCheckingRestore(false);
87
+ }
88
+ }, [repo, rest]);
89
+ (0, react_1.useEffect)(() => {
90
+ let alive = true;
91
+ repo
92
+ .loadDevice()
93
+ .then(async (persisted) => {
94
+ if (!alive || persisted)
95
+ return; // have local state → not evicted; skip the server call
96
+ const model = await rest.getKeyBackup();
97
+ if (alive)
98
+ setNeedsRestore(model !== null);
99
+ })
100
+ .catch((err) => {
101
+ if (alive)
102
+ setError(err); // fail soft
103
+ });
104
+ return () => {
105
+ alive = false;
106
+ };
107
+ }, [repo, rest]);
108
+ const backup = (0, react_1.useCallback)(async (passphrase) => {
109
+ setBackingUp(true);
110
+ setError(null);
111
+ try {
112
+ const b = await crypto.exportBackup(passphrase);
113
+ const { updatedAt } = await rest.uploadKeyBackup({
114
+ // base64 at the wire boundary; KDF params (incl. the hex salt) are non-secret metadata.
115
+ blob: (0, base64_js_1.toBase64)(b.blob),
116
+ nonce: (0, base64_js_1.toBase64)(b.nonce),
117
+ kdf: b.kdf,
118
+ kdfParams: b.kdfParams,
119
+ cipher: b.cipher,
120
+ version: b.version,
121
+ });
122
+ setLastBackupAt(updatedAt);
123
+ setNeedsBackup(false);
124
+ }
125
+ catch (err) {
126
+ setError(err);
127
+ throw err;
128
+ }
129
+ finally {
130
+ setBackingUp(false);
131
+ }
132
+ }, [crypto, rest]);
133
+ const restore = (0, react_1.useCallback)(async (passphrase) => {
134
+ setRestoring(true);
135
+ setError(null);
136
+ try {
137
+ const model = await rest.getKeyBackup();
138
+ if (!model)
139
+ throw new Error("No key backup found on the server to restore.");
140
+ // Decrypt + repopulate crypto memory (identity + groups). Returns the restored identity.
141
+ const id = await crypto.importBackup(passphrase, {
142
+ blob: (0, base64_js_1.fromBase64)(model.blob),
143
+ nonce: (0, base64_js_1.fromBase64)(model.nonce),
144
+ kdf: model.kdf,
145
+ kdfParams: model.kdfParams,
146
+ cipher: model.cipher,
147
+ version: model.version,
148
+ });
149
+ // Re-assert the (already-registered) device to recover its server row — idempotent on
150
+ // (userId, deviceId). The row's id is the senderDeviceId messages need.
151
+ const device = await rest.registerDevice({
152
+ deviceId: id.deviceId,
153
+ signaturePublicKey: (0, base64_js_1.toBase64)(id.signaturePublicKey),
154
+ credential: (0, base64_js_1.toBase64)(id.credential),
155
+ ciphersuite: id.ciphersuite,
156
+ });
157
+ await repo.saveDevice({
158
+ deviceId: id.deviceId,
159
+ deviceState: await crypto.exportDeviceState(),
160
+ device,
161
+ });
162
+ // Rebind conversationId → restored group state from the server's conversation list.
163
+ let cursor;
164
+ for (;;) {
165
+ const page = await rest.listConversations(cursor ? { cursor } : undefined);
166
+ for (const c of page.conversations) {
167
+ try {
168
+ await rememberGroup(c.id, {
169
+ mlsGroupId: (0, base64_js_1.fromBase64)(c.mlsGroupId),
170
+ epoch: BigInt(c.currentEpoch),
171
+ });
172
+ }
173
+ catch {
174
+ // The group isn't in this backup (created after it, or we're not a member) — skip it.
175
+ }
176
+ }
177
+ if (!page.hasMore || page.conversations.length === 0)
178
+ break;
179
+ const last = page.conversations[page.conversations.length - 1];
180
+ cursor = last.lastMessageAt ?? last.createdAt;
181
+ }
182
+ setNeedsBackup(false);
183
+ setNeedsRestore(false); // we now hold local key material again
184
+ }
185
+ catch (err) {
186
+ setError(err);
187
+ throw err;
188
+ }
189
+ finally {
190
+ setRestoring(false);
191
+ }
192
+ }, [crypto, rest, repo, rememberGroup]);
193
+ return {
194
+ backup,
195
+ restore,
196
+ backingUp,
197
+ restoring,
198
+ error,
199
+ lastBackupAt,
200
+ needsBackup,
201
+ needsRestore,
202
+ checkingRestore,
203
+ recheckRestore,
204
+ estimateStrength: passphrase_strength_js_1.estimatePassphraseStrength,
205
+ };
206
+ }
207
+ //# sourceMappingURL=useSecureBackup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSecureBackup.js","sourceRoot":"","sources":["../../../src/hooks/useSecureBackup.tsx"],"names":[],"mappings":"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"}
@@ -8,7 +8,20 @@ export interface UseSecureDeviceOptions {
8
8
  ciphersuite?: number;
9
9
  /** How many KeyPackages to publish on registration and replenish toward. Default 20. */
10
10
  keyPackageTarget?: number;
11
- /** Auto-replenish when `secure:key-packages-low` fires. Default true. */
11
+ /**
12
+ * Low-water mark: when the server's available count drops **below** this, a top-up refills to
13
+ * {@link UseSecureDeviceOptions.keyPackageTarget}. KeyPackages are single-use, so this guards
14
+ * against exhaustion (peers unable to add this device). Default `ceil(keyPackageTarget / 2)` (10 for
15
+ * the default target of 20). Only governs the proactive/manual path — the server's
16
+ * `secure:key-packages-low` signal always tops up, since the server already judged the stock low.
17
+ */
18
+ keyPackageLowWater?: number;
19
+ /**
20
+ * Auto-replenish without app involvement. Default true. When true the hook subscribes to
21
+ * `secure:key-packages-low` and runs a one-shot proactive count check once the device is ready. When
22
+ * false, both automatic paths are disabled but {@link UseSecureDeviceValues.checkAndReplenish}
23
+ * still works on demand.
24
+ */
12
25
  autoReplenish?: boolean;
13
26
  }
14
27
  /** The state and actions returned by {@link useSecureDevice}. */
@@ -29,6 +42,13 @@ export interface UseSecureDeviceValues {
29
42
  publishKeyPackages: (count?: number) => Promise<number>;
30
43
  /** Re-query the server for the available KeyPackage count and update `keyPackagesAvailable`. */
31
44
  refreshKeyPackageCount: () => Promise<number>;
45
+ /**
46
+ * Refresh the server count and, if it's below the low-water mark, top up to `keyPackageTarget`
47
+ * (publishing only the deficit). Safe to call before {@link UseSecureDeviceValues.register} — it
48
+ * no-ops to 0 when there's no device and never throws for that case — so an app can wire it to a
49
+ * window-focus / app-foreground handler.
50
+ */
51
+ checkAndReplenish: () => Promise<number>;
32
52
  }
33
53
  /**
34
54
  * Register this client as an MLS device, persist its identity, and keep KeyPackages stocked.
@@ -4,7 +4,14 @@
4
4
  //
5
5
  // On mount it re-hydrates a persisted device (stable deviceId + private state via
6
6
  // crypto.importDeviceState) so a reload does NOT mint a new identity. register() generates, registers
7
- // on the server, and persists. Replenishes on the `secure:key-packages-low` realtime signal.
7
+ // on the server, and persists.
8
+ //
9
+ // KeyPackages are single-use (the server consumes one per group-add), so running dry means peers can't
10
+ // add this device. The hook keeps the stock topped up to `keyPackageTarget` from three triggers: the
11
+ // server's `secure:key-packages-low` realtime signal, a one-shot proactive count check once the device
12
+ // is ready (self-heals a client that missed the signal while offline), and an app-callable
13
+ // `checkAndReplenish()` (e.g. on window focus). Each top-up publishes only the DEFICIT to the target
14
+ // (using the actual available count), not a blind full batch.
8
15
  Object.defineProperty(exports, "__esModule", { value: true });
9
16
  exports.useSecureDevice = useSecureDevice;
10
17
  const react_1 = require("react");
@@ -40,6 +47,7 @@ function newDeviceId() {
40
47
  function useSecureDevice(options = {}) {
41
48
  const { crypto, rest, socket, repo } = (0, secure_chat_context_js_1.useSecureChat)();
42
49
  const { ciphersuite, keyPackageTarget = 20, autoReplenish = true } = options;
50
+ const keyPackageLowWater = options.keyPackageLowWater ?? Math.ceil(keyPackageTarget / 2);
43
51
  const [device, setDevice] = (0, react_1.useState)(null);
44
52
  const [loading, setLoading] = (0, react_1.useState)(true);
45
53
  const [registering, setRegistering] = (0, react_1.useState)(false);
@@ -47,6 +55,8 @@ function useSecureDevice(options = {}) {
47
55
  const [keyPackagesAvailable, setKeyPackagesAvailable] = (0, react_1.useState)(null);
48
56
  const deviceIdRef = (0, react_1.useRef)(options.deviceId ?? newDeviceId());
49
57
  const registerStartedRef = (0, react_1.useRef)(false);
58
+ // Guards the one-shot proactive count check so it runs once per device-ready, not on every render.
59
+ const proactiveCheckedRef = (0, react_1.useRef)(false);
50
60
  // On mount: re-hydrate a persisted device (stable id + private state). No persisted device ⇒
51
61
  // first-run; the app calls register().
52
62
  (0, react_1.useEffect)(() => {
@@ -99,6 +109,25 @@ function useSecureDevice(options = {}) {
99
109
  setKeyPackagesAvailable(available);
100
110
  return available;
101
111
  }, [rest, device]);
112
+ // Publish only the shortfall (target − available) to refill to the target; nothing if already
113
+ // at/above it. Optimistically bumps the local count so the UI reflects the refill without a re-query.
114
+ // Callers guarantee `device` exists (publishKeyPackages throws otherwise).
115
+ const replenishToTarget = (0, react_1.useCallback)(async (available) => {
116
+ const deficit = keyPackageTarget - available;
117
+ if (deficit <= 0)
118
+ return 0;
119
+ const published = await publishKeyPackages(deficit);
120
+ setKeyPackagesAvailable(available + published);
121
+ return published;
122
+ }, [keyPackageTarget, publishKeyPackages]);
123
+ const checkAndReplenish = (0, react_1.useCallback)(async () => {
124
+ if (!device)
125
+ return 0; // not registered yet — no-op (app may call this eagerly on focus)
126
+ const available = await refreshKeyPackageCount();
127
+ if (available >= keyPackageLowWater)
128
+ return 0;
129
+ return replenishToTarget(available);
130
+ }, [device, refreshKeyPackageCount, keyPackageLowWater, replenishToTarget]);
102
131
  const register = (0, react_1.useCallback)(async () => {
103
132
  registerStartedRef.current = true;
104
133
  setRegistering(true);
@@ -128,17 +157,28 @@ function useSecureDevice(options = {}) {
128
157
  setRegistering(false);
129
158
  }
130
159
  }, [crypto, rest, repo, ciphersuite]);
131
- // Auto-replenish on the server's low-water signal for this device.
160
+ // Auto-replenish on the server's low-water signal for this device. We top up to the target using the
161
+ // count the signal reports (not a blind full batch), and trust the server's "low" verdict — the
162
+ // client `keyPackageLowWater` only gates the proactive path below.
132
163
  (0, react_1.useEffect)(() => {
133
164
  if (!autoReplenish || !device)
134
165
  return;
135
166
  const off = socket.on("secure:key-packages-low", (signal) => {
136
167
  if (signal.deviceId !== device.id)
137
- return;
138
- publishKeyPackages().catch(setError);
168
+ return; // device.id is the server ROW id, not the deviceId
169
+ setKeyPackagesAvailable(signal.available);
170
+ replenishToTarget(signal.available).catch(setError);
139
171
  });
140
172
  return off;
141
- }, [autoReplenish, device, socket, publishKeyPackages]);
173
+ }, [autoReplenish, device, socket, replenishToTarget]);
174
+ // One-shot proactive top-up once the device is ready (covers both register and rehydrate-on-mount),
175
+ // so a client that missed the realtime signal while offline self-heals on next load.
176
+ (0, react_1.useEffect)(() => {
177
+ if (!autoReplenish || !device || proactiveCheckedRef.current)
178
+ return;
179
+ proactiveCheckedRef.current = true;
180
+ checkAndReplenish().catch(setError);
181
+ }, [autoReplenish, device, checkAndReplenish]);
142
182
  return {
143
183
  device,
144
184
  loading,
@@ -148,6 +188,7 @@ function useSecureDevice(options = {}) {
148
188
  register,
149
189
  publishKeyPackages,
150
190
  refreshKeyPackageCount,
191
+ checkAndReplenish,
151
192
  };
152
193
  }
153
194
  //# sourceMappingURL=useSecureDevice.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSecureDevice.js","sourceRoot":"","sources":["../../../src/hooks/useSecureDevice.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSecureDevice.js","sourceRoot":"","sources":["../../../src/hooks/useSecureDevice.tsx"],"names":[],"mappings":"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"}