@oxy.so/core 4.5.0-oxy-one.1 → 4.6.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 (283) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/HttpService.js +63 -19
  3. package/dist/cjs/api/accounts.js +10 -3
  4. package/dist/cjs/api/agency.js +3 -1
  5. package/dist/cjs/api/apps.js +15 -4
  6. package/dist/cjs/api/assets.js +39 -11
  7. package/dist/cjs/api/auth.js +28 -8
  8. package/dist/cjs/api/billing.js +24 -7
  9. package/dist/cjs/api/civic.js +14 -4
  10. package/dist/cjs/api/follows.js +9 -2
  11. package/dist/cjs/api/identity.js +31 -9
  12. package/dist/cjs/api/nodes.js +3 -1
  13. package/dist/cjs/api/privacy.js +8 -2
  14. package/dist/cjs/api/session.js +15 -4
  15. package/dist/cjs/api/store.js +15 -5
  16. package/dist/cjs/api/topics.js +10 -3
  17. package/dist/cjs/api/users.js +16 -4
  18. package/dist/cjs/boot/sessionColdBoot.js +40 -13
  19. package/dist/cjs/constants/version.js +6 -6
  20. package/dist/cjs/crypto/identityMarker.js +3 -1
  21. package/dist/cjs/crypto/identityProof.js +6 -1
  22. package/dist/cjs/crypto/keyManager.js +87 -32
  23. package/dist/cjs/crypto/recoveryPhrase.js +2 -2
  24. package/dist/cjs/crypto/signatureService.js +3 -4
  25. package/dist/cjs/i18n/index.js +16 -3
  26. package/dist/cjs/i18n/locales/ar-SA.js +366 -366
  27. package/dist/cjs/i18n/locales/ca-ES.js +366 -366
  28. package/dist/cjs/i18n/locales/de-DE.js +366 -366
  29. package/dist/cjs/i18n/locales/en-US.js +1994 -1994
  30. package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
  31. package/dist/cjs/i18n/locales/fr-FR.js +366 -366
  32. package/dist/cjs/i18n/locales/it-IT.js +366 -366
  33. package/dist/cjs/i18n/locales/ja-JP.js +366 -366
  34. package/dist/cjs/i18n/locales/ko-KR.js +366 -366
  35. package/dist/cjs/i18n/locales/pt-PT.js +366 -366
  36. package/dist/cjs/i18n/locales/zh-CN.js +366 -366
  37. package/dist/cjs/inference/OxyInferenceClient.js +12 -18
  38. package/dist/cjs/models/interfaces.js +11 -11
  39. package/dist/cjs/server/OxyServer.js +86 -28
  40. package/dist/cjs/server/agentAccount.js +6 -2
  41. package/dist/cjs/server/aliaMachineCredential.js +39 -14
  42. package/dist/cjs/server/auth.js +5 -4
  43. package/dist/cjs/server/capabilityTicket.js +5 -5
  44. package/dist/cjs/server/index.js +2 -1
  45. package/dist/cjs/server/liveCapabilityTicket.js +4 -1
  46. package/dist/cjs/server/middleware.js +61 -42
  47. package/dist/cjs/server/namespaces.js +28 -7
  48. package/dist/cjs/server/requesterAssertion.js +36 -30
  49. package/dist/cjs/server/serviceTokenLifetime.js +7 -4
  50. package/dist/cjs/server/traffic.js +53 -14
  51. package/dist/cjs/server/trafficNodeHttp.js +33 -8
  52. package/dist/cjs/server/workloadIdentity.client.js +4 -0
  53. package/dist/cjs/server/workloadIdentity.js +28 -1
  54. package/dist/cjs/session/SessionClient.js +54 -17
  55. package/dist/cjs/session/accountDelegationAccess.js +4 -6
  56. package/dist/cjs/session/accountDialogController.js +1 -1
  57. package/dist/cjs/session/authStateStore.js +18 -9
  58. package/dist/cjs/session/identityPin.js +6 -2
  59. package/dist/cjs/session/refresh.js +81 -23
  60. package/dist/cjs/session/sharedDeviceCredential.js +28 -15
  61. package/dist/cjs/shared/utils/errorUtils.js +1 -1
  62. package/dist/cjs/shared/utils/networkUtils.js +1 -1
  63. package/dist/cjs/shared/utils/themeUtils.js +1 -1
  64. package/dist/cjs/utils/accountCacheSweep.js +3 -1
  65. package/dist/cjs/utils/accountUtils.js +1 -3
  66. package/dist/cjs/utils/asyncUtils.js +1 -1
  67. package/dist/cjs/utils/authHelpers.js +2 -1
  68. package/dist/cjs/utils/avatarUtils.js +4 -2
  69. package/dist/cjs/utils/cache.js +1 -1
  70. package/dist/cjs/utils/cacheKey.js +3 -1
  71. package/dist/cjs/utils/deviceManager.js +4 -4
  72. package/dist/cjs/utils/errorUtils.js +6 -4
  73. package/dist/cjs/utils/languageUtils.js +404 -57
  74. package/dist/cjs/utils/profileLinks.js +1 -3
  75. package/dist/cjs/utils/requestUtils.js +7 -1
  76. package/dist/cjs/utils/userIdentity.js +1 -1
  77. package/dist/cjs/utils/validationUtils.js +5 -6
  78. package/dist/esm/HttpService.js +64 -20
  79. package/dist/esm/api/accounts.js +11 -4
  80. package/dist/esm/api/agency.js +3 -1
  81. package/dist/esm/api/apps.js +15 -4
  82. package/dist/esm/api/assets.js +39 -11
  83. package/dist/esm/api/auth.js +28 -8
  84. package/dist/esm/api/billing.js +25 -8
  85. package/dist/esm/api/civic.js +14 -4
  86. package/dist/esm/api/follows.js +9 -2
  87. package/dist/esm/api/identity.js +31 -9
  88. package/dist/esm/api/nodes.js +3 -1
  89. package/dist/esm/api/privacy.js +8 -2
  90. package/dist/esm/api/session.js +15 -4
  91. package/dist/esm/api/store.js +15 -5
  92. package/dist/esm/api/topics.js +10 -3
  93. package/dist/esm/api/users.js +16 -4
  94. package/dist/esm/boot/sessionColdBoot.js +41 -14
  95. package/dist/esm/constants/version.js +6 -6
  96. package/dist/esm/crypto/identityMarker.js +3 -1
  97. package/dist/esm/crypto/identityProof.js +6 -1
  98. package/dist/esm/crypto/internal.js +1 -1
  99. package/dist/esm/crypto/keyManager.js +87 -32
  100. package/dist/esm/crypto/recoveryPhrase.js +2 -2
  101. package/dist/esm/crypto/signatureService.js +3 -4
  102. package/dist/esm/i18n/index.js +16 -3
  103. package/dist/esm/i18n/locales/ar-SA.js +366 -366
  104. package/dist/esm/i18n/locales/ca-ES.js +366 -366
  105. package/dist/esm/i18n/locales/de-DE.js +366 -366
  106. package/dist/esm/i18n/locales/en-US.js +1994 -1994
  107. package/dist/esm/i18n/locales/es-ES.js +1994 -1994
  108. package/dist/esm/i18n/locales/fr-FR.js +366 -366
  109. package/dist/esm/i18n/locales/it-IT.js +366 -366
  110. package/dist/esm/i18n/locales/ja-JP.js +366 -366
  111. package/dist/esm/i18n/locales/ko-KR.js +366 -366
  112. package/dist/esm/i18n/locales/pt-PT.js +366 -366
  113. package/dist/esm/i18n/locales/zh-CN.js +366 -366
  114. package/dist/esm/index.js +1 -1
  115. package/dist/esm/inference/OxyInferenceClient.js +13 -19
  116. package/dist/esm/models/interfaces.js +11 -11
  117. package/dist/esm/server/OxyServer.js +87 -29
  118. package/dist/esm/server/agentAccount.js +6 -2
  119. package/dist/esm/server/aliaMachineCredential.js +39 -14
  120. package/dist/esm/server/auth.js +6 -5
  121. package/dist/esm/server/capabilityTicket.js +6 -6
  122. package/dist/esm/server/index.js +5 -5
  123. package/dist/esm/server/liveCapabilityTicket.js +6 -3
  124. package/dist/esm/server/middleware.js +62 -43
  125. package/dist/esm/server/namespaces.js +31 -10
  126. package/dist/esm/server/requesterAssertion.js +36 -30
  127. package/dist/esm/server/serviceTokenLifetime.js +7 -4
  128. package/dist/esm/server/traffic.js +55 -16
  129. package/dist/esm/server/trafficNodeHttp.js +34 -9
  130. package/dist/esm/server/userInvalidation.js +1 -1
  131. package/dist/esm/server/workloadIdentity.client.js +3 -0
  132. package/dist/esm/server/workloadIdentity.js +27 -1
  133. package/dist/esm/session/SessionClient.js +54 -17
  134. package/dist/esm/session/accountDelegationAccess.js +5 -7
  135. package/dist/esm/session/accountDialogController.js +1 -1
  136. package/dist/esm/session/authStateStore.js +18 -9
  137. package/dist/esm/session/identityPin.js +6 -2
  138. package/dist/esm/session/refresh.js +82 -24
  139. package/dist/esm/session/sharedDeviceCredential.js +28 -15
  140. package/dist/esm/shared/utils/errorUtils.js +1 -1
  141. package/dist/esm/shared/utils/networkUtils.js +1 -1
  142. package/dist/esm/shared/utils/themeUtils.js +1 -1
  143. package/dist/esm/utils/accountCacheSweep.js +3 -1
  144. package/dist/esm/utils/accountUtils.js +1 -3
  145. package/dist/esm/utils/asyncUtils.js +1 -1
  146. package/dist/esm/utils/authHelpers.js +2 -1
  147. package/dist/esm/utils/avatarUtils.js +4 -2
  148. package/dist/esm/utils/cache.js +1 -1
  149. package/dist/esm/utils/cacheKey.js +3 -1
  150. package/dist/esm/utils/deviceManager.js +4 -4
  151. package/dist/esm/utils/errorUtils.js +6 -4
  152. package/dist/esm/utils/languageUtils.js +404 -57
  153. package/dist/esm/utils/profileLinks.js +1 -3
  154. package/dist/esm/utils/requestUtils.js +7 -1
  155. package/dist/esm/utils/userIdentity.js +1 -1
  156. package/dist/esm/utils/validationUtils.js +5 -6
  157. package/dist/types/HttpService.d.ts +1 -1
  158. package/dist/types/api/accounts.d.ts +16 -3
  159. package/dist/types/api/apps.d.ts +4 -4
  160. package/dist/types/crypto/internal.d.ts +1 -1
  161. package/dist/types/index.d.ts +4 -4
  162. package/dist/types/models/interfaces.d.ts +9 -1
  163. package/dist/types/server/index.d.ts +6 -6
  164. package/dist/types/server/workloadIdentity.client.d.ts +5 -1
  165. package/dist/types/server/workloadIdentity.d.ts +26 -0
  166. package/dist/types/session/accountDelegationAccess.d.ts +1 -1
  167. package/dist/types/session/index.d.ts +1 -1
  168. package/dist/types/utils/userIdentity.d.ts +1 -1
  169. package/dist/types/utils/validationUtils.d.ts +4 -5
  170. package/package.json +3 -4
  171. package/src/HttpService.ts +183 -53
  172. package/src/OxyServices.errors.ts +7 -3
  173. package/src/OxyServices.ts +6 -2
  174. package/src/api/accounts.ts +96 -28
  175. package/src/api/agency.ts +24 -9
  176. package/src/api/appData.ts +12 -2
  177. package/src/api/apps.ts +81 -31
  178. package/src/api/assets.ts +113 -43
  179. package/src/api/auth.ts +145 -77
  180. package/src/api/billing.ts +112 -65
  181. package/src/api/civic.ts +61 -19
  182. package/src/api/contacts.ts +4 -1
  183. package/src/api/devices.ts +54 -21
  184. package/src/api/follows.ts +88 -31
  185. package/src/api/identity.ts +101 -33
  186. package/src/api/linkedAccounts.ts +26 -8
  187. package/src/api/nodes.ts +18 -6
  188. package/src/api/notifications.ts +26 -6
  189. package/src/api/privacy.ts +42 -8
  190. package/src/api/reputation.ts +18 -7
  191. package/src/api/session.ts +23 -5
  192. package/src/api/store.ts +81 -31
  193. package/src/api/topics.ts +18 -6
  194. package/src/api/users.ts +98 -35
  195. package/src/boot/sessionColdBoot.ts +60 -19
  196. package/src/civic/payloads.ts +1 -4
  197. package/src/client/context.ts +6 -1
  198. package/src/constants/version.ts +7 -6
  199. package/src/crypto/aead.ts +1 -5
  200. package/src/crypto/ecdh.ts +1 -4
  201. package/src/crypto/identityDerivations.ts +5 -1
  202. package/src/crypto/identityMarker.ts +3 -1
  203. package/src/crypto/identityProof.ts +6 -1
  204. package/src/crypto/index.ts +11 -11
  205. package/src/crypto/internal.ts +6 -1
  206. package/src/crypto/keyManager.ts +348 -98
  207. package/src/crypto/recoveryPhrase.ts +14 -9
  208. package/src/crypto/registrationPow.ts +1 -1
  209. package/src/crypto/signatureService.ts +14 -6
  210. package/src/i18n/accountCategoryLabels.ts +1 -4
  211. package/src/i18n/accountRoleLabels.ts +1 -4
  212. package/src/i18n/index.ts +25 -7
  213. package/src/i18n/locales/ar-SA.ts +375 -366
  214. package/src/i18n/locales/ca-ES.ts +382 -366
  215. package/src/i18n/locales/de-DE.ts +387 -366
  216. package/src/i18n/locales/en-US.ts +2034 -1994
  217. package/src/i18n/locales/es-ES.ts +2043 -1994
  218. package/src/i18n/locales/fr-FR.ts +387 -366
  219. package/src/i18n/locales/it-IT.ts +380 -366
  220. package/src/i18n/locales/ja-JP.ts +380 -366
  221. package/src/i18n/locales/ko-KR.ts +377 -366
  222. package/src/i18n/locales/pt-PT.ts +383 -366
  223. package/src/i18n/locales/zh-CN.ts +373 -366
  224. package/src/i18n/trustTierLabels.ts +1 -4
  225. package/src/index.ts +371 -349
  226. package/src/inference/OxyInferenceClient.ts +784 -780
  227. package/src/models/interfaces.ts +39 -27
  228. package/src/server/OxyServer.ts +227 -68
  229. package/src/server/agentAccount.ts +8 -2
  230. package/src/server/aliaMachineCredential.ts +57 -16
  231. package/src/server/auth.ts +9 -5
  232. package/src/server/capabilityTicket.ts +30 -21
  233. package/src/server/index.ts +29 -6
  234. package/src/server/liveCapabilityTicket.ts +31 -9
  235. package/src/server/middleware.ts +212 -121
  236. package/src/server/namespaces.ts +247 -94
  237. package/src/server/rateLimit.ts +2 -4
  238. package/src/server/requesterAssertion.ts +93 -54
  239. package/src/server/serviceTokenLifetime.ts +9 -4
  240. package/src/server/traffic.ts +117 -23
  241. package/src/server/trafficNodeHttp.ts +61 -14
  242. package/src/server/userInvalidation.ts +1 -4
  243. package/src/server/workloadIdentity.client.ts +12 -1
  244. package/src/server/workloadIdentity.ts +52 -5
  245. package/src/session/SessionClient.ts +170 -49
  246. package/src/session/accountDelegationAccess.ts +24 -31
  247. package/src/session/accountDialogController.ts +59 -14
  248. package/src/session/accountSwitchTargets.ts +10 -15
  249. package/src/session/authStateStore.ts +121 -81
  250. package/src/session/deviceDirectory.ts +1 -3
  251. package/src/session/identityPin.ts +10 -11
  252. package/src/session/identitySession.ts +3 -1
  253. package/src/session/index.ts +87 -82
  254. package/src/session/refresh.ts +138 -44
  255. package/src/session/sessionClientHost.ts +1 -3
  256. package/src/session/sharedDeviceCredential.ts +35 -14
  257. package/src/session/socketLoader.ts +9 -2
  258. package/src/shared/utils/errorUtils.ts +5 -2
  259. package/src/shared/utils/networkUtils.ts +5 -8
  260. package/src/shared/utils/themeUtils.ts +2 -6
  261. package/src/types/buffer.d.ts +66 -21
  262. package/src/types/expo-secure-store.d.ts +5 -1
  263. package/src/utils/accountCacheSweep.ts +4 -5
  264. package/src/utils/accountUtils.ts +107 -108
  265. package/src/utils/apiUtils.ts +3 -3
  266. package/src/utils/asyncUtils.ts +2 -2
  267. package/src/utils/authHelpers.ts +10 -5
  268. package/src/utils/avatarUtils.ts +11 -6
  269. package/src/utils/cache.ts +9 -10
  270. package/src/utils/cacheKey.ts +5 -1
  271. package/src/utils/coldBoot.ts +1 -3
  272. package/src/utils/commonsDelivery.ts +1 -4
  273. package/src/utils/deviceManager.ts +15 -14
  274. package/src/utils/errorUtils.ts +68 -86
  275. package/src/utils/languageUtils.ts +407 -58
  276. package/src/utils/oauthPkce.ts +1 -4
  277. package/src/utils/platform.ts +0 -1
  278. package/src/utils/profileLinks.ts +2 -5
  279. package/src/utils/requestUtils.ts +18 -14
  280. package/src/utils/sessionUtils.ts +34 -33
  281. package/src/utils/userHandle.ts +3 -1
  282. package/src/utils/userIdentity.ts +1 -1
  283. package/src/utils/validationUtils.ts +11 -9
@@ -93,7 +93,7 @@ export class IdentityAlreadyExistsError extends Error {
93
93
  super(
94
94
  'An identity already exists on this device. Refusing to overwrite without explicit consent. ' +
95
95
  'If you really want to replace it, ensure the user has saved their recovery phrase, then call ' +
96
- 'the operation with { overwrite: true }.'
96
+ 'the operation with { overwrite: true }.',
97
97
  );
98
98
  this.existingPublicKey = existingPublicKey;
99
99
  }
@@ -107,7 +107,10 @@ export class IdentityAlreadyExistsError extends Error {
107
107
  */
108
108
  export class IdentityPersistError extends Error {
109
109
  override readonly name = 'IdentityPersistError';
110
- constructor(message: string, readonly cause?: unknown) {
110
+ constructor(
111
+ message: string,
112
+ readonly cause?: unknown,
113
+ ) {
111
114
  super(message);
112
115
  }
113
116
  }
@@ -126,7 +129,10 @@ export class IdentityPersistError extends Error {
126
129
  */
127
130
  export class IdentityUnavailableError extends Error {
128
131
  override readonly name = 'IdentityUnavailableError';
129
- constructor(message: string, readonly cause?: unknown) {
132
+ constructor(
133
+ message: string,
134
+ readonly cause?: unknown,
135
+ ) {
130
136
  super(message);
131
137
  }
132
138
  }
@@ -334,7 +340,7 @@ async function initExpoCrypto(): Promise<ExpoCryptoLike> {
334
340
  */
335
341
  function uint8ArrayToHex(bytes: Uint8Array): string {
336
342
  return Array.from(bytes)
337
- .map(b => b.toString(16).padStart(2, '0'))
343
+ .map((b) => b.toString(16).padStart(2, '0'))
338
344
  .join('');
339
345
  }
340
346
 
@@ -356,7 +362,7 @@ async function getSecureRandomBytes(length: number): Promise<Uint8Array> {
356
362
  const Crypto = await initExpoCrypto();
357
363
  return Crypto.getRandomBytes(length);
358
364
  }
359
-
365
+
360
366
  // In Node.js, use Node's crypto module.
361
367
  //
362
368
  // `loadNodeCrypto` is per-platform: the default variant performs
@@ -499,7 +505,9 @@ export class KeyManager {
499
505
  store: Awaited<ReturnType<typeof initSecureStore>>,
500
506
  service?: string,
501
507
  ): OxySecureStoreOptions {
502
- const opts: OxySecureStoreOptions = { keychainAccessible: store.WHEN_UNLOCKED_THIS_DEVICE_ONLY };
508
+ const opts: OxySecureStoreOptions = {
509
+ keychainAccessible: store.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
510
+ };
503
511
  if (service) {
504
512
  opts.keychainService = service;
505
513
  }
@@ -566,7 +574,11 @@ export class KeyManager {
566
574
  } catch (error) {
567
575
  // Advisory only — a failed write just re-runs the cheap slot check next launch.
568
576
  if (isDev()) {
569
- logger.debug('Failed to set slots-migrated flag (advisory)', { component: 'KeyManager' }, error);
577
+ logger.debug(
578
+ 'Failed to set slots-migrated flag (advisory)',
579
+ { component: 'KeyManager' },
580
+ error,
581
+ );
570
582
  }
571
583
  }
572
584
  }
@@ -741,7 +753,11 @@ export class KeyManager {
741
753
  backupPublic = await store.getItemAsync(STORAGE_KEYS.BACKUP_PUBLIC_KEY);
742
754
  } catch (error) {
743
755
  if (isDev()) {
744
- logger.debug('Legacy backup unreadable during migration (non-fatal)', { component: 'KeyManager' }, error);
756
+ logger.debug(
757
+ 'Legacy backup unreadable during migration (non-fatal)',
758
+ { component: 'KeyManager' },
759
+ error,
760
+ );
745
761
  }
746
762
  backupPrivate = null;
747
763
  backupPublic = null;
@@ -773,7 +789,11 @@ export class KeyManager {
773
789
  KeyManager._slotOpts(V2_BACKUP_KEYCHAIN_SERVICE),
774
790
  );
775
791
  } catch (error) {
776
- logger.warn('Failed to migrate identity backup slot to v2 (non-fatal)', { component: 'KeyManager' }, error);
792
+ logger.warn(
793
+ 'Failed to migrate identity backup slot to v2 (non-fatal)',
794
+ { component: 'KeyManager' },
795
+ error,
796
+ );
777
797
  }
778
798
  }
779
799
 
@@ -795,8 +815,16 @@ export class KeyManager {
795
815
  private static async _bestEffortDeleteV2Primary(
796
816
  store: Awaited<ReturnType<typeof initSecureStore>>,
797
817
  ): Promise<void> {
798
- await KeyManager._bestEffortDelete(store, V2_STORAGE_KEYS.PRIVATE_KEY, V2_PRIMARY_KEYCHAIN_SERVICE);
799
- await KeyManager._bestEffortDelete(store, V2_STORAGE_KEYS.PUBLIC_KEY, V2_PRIMARY_KEYCHAIN_SERVICE);
818
+ await KeyManager._bestEffortDelete(
819
+ store,
820
+ V2_STORAGE_KEYS.PRIVATE_KEY,
821
+ V2_PRIMARY_KEYCHAIN_SERVICE,
822
+ );
823
+ await KeyManager._bestEffortDelete(
824
+ store,
825
+ V2_STORAGE_KEYS.PUBLIC_KEY,
826
+ V2_PRIMARY_KEYCHAIN_SERVICE,
827
+ );
800
828
  }
801
829
 
802
830
  private static async _bestEffortDeleteLegacyPrimaryAndBackup(
@@ -812,9 +840,21 @@ export class KeyManager {
812
840
  private static async _bestEffortDeleteBackupsAllGenerations(
813
841
  store: Awaited<ReturnType<typeof initSecureStore>>,
814
842
  ): Promise<void> {
815
- await KeyManager._bestEffortDelete(store, V2_STORAGE_KEYS.BACKUP_PRIVATE_KEY, V2_BACKUP_KEYCHAIN_SERVICE);
816
- await KeyManager._bestEffortDelete(store, V2_STORAGE_KEYS.BACKUP_PUBLIC_KEY, V2_BACKUP_KEYCHAIN_SERVICE);
817
- await KeyManager._bestEffortDelete(store, V2_STORAGE_KEYS.BACKUP_TIMESTAMP, V2_BACKUP_KEYCHAIN_SERVICE);
843
+ await KeyManager._bestEffortDelete(
844
+ store,
845
+ V2_STORAGE_KEYS.BACKUP_PRIVATE_KEY,
846
+ V2_BACKUP_KEYCHAIN_SERVICE,
847
+ );
848
+ await KeyManager._bestEffortDelete(
849
+ store,
850
+ V2_STORAGE_KEYS.BACKUP_PUBLIC_KEY,
851
+ V2_BACKUP_KEYCHAIN_SERVICE,
852
+ );
853
+ await KeyManager._bestEffortDelete(
854
+ store,
855
+ V2_STORAGE_KEYS.BACKUP_TIMESTAMP,
856
+ V2_BACKUP_KEYCHAIN_SERVICE,
857
+ );
818
858
  await KeyManager._bestEffortDelete(store, STORAGE_KEYS.BACKUP_PRIVATE_KEY);
819
859
  await KeyManager._bestEffortDelete(store, STORAGE_KEYS.BACKUP_PUBLIC_KEY);
820
860
  await KeyManager._bestEffortDelete(store, STORAGE_KEYS.BACKUP_TIMESTAMP);
@@ -837,7 +877,11 @@ export class KeyManager {
837
877
  }
838
878
  KeyManager.invalidateSharedCache();
839
879
  } catch (error) {
840
- logger.warn('Failed to clear shared identity slot during force delete', { component: 'KeyManager' }, error);
880
+ logger.warn(
881
+ 'Failed to clear shared identity slot during force delete',
882
+ { component: 'KeyManager' },
883
+ error,
884
+ );
841
885
  }
842
886
  }
843
887
 
@@ -850,7 +894,9 @@ export class KeyManager {
850
894
  );
851
895
  }
852
896
  if (!(await signer.write(privateKey, publicKey))) {
853
- throw new IdentityPersistError(`The identity signer store (${signer.name}) did not confirm the write.`);
897
+ throw new IdentityPersistError(
898
+ `The identity signer store (${signer.name}) did not confirm the write.`,
899
+ );
854
900
  }
855
901
  }
856
902
 
@@ -863,7 +909,11 @@ export class KeyManager {
863
909
  try {
864
910
  await signer.clear();
865
911
  } catch (error) {
866
- logger.warn(`Failed to clear the identity signer store (${signer.name})`, { component: 'KeyManager' }, error);
912
+ logger.warn(
913
+ `Failed to clear the identity signer store (${signer.name})`,
914
+ { component: 'KeyManager' },
915
+ error,
916
+ );
867
917
  } finally {
868
918
  KeyManager.invalidateSharedCache();
869
919
  }
@@ -882,10 +932,16 @@ export class KeyManager {
882
932
  }
883
933
  try {
884
934
  if (!(await signer.write(privateKey, publicKey))) {
885
- logger.warn(`The identity signer store (${signer.name}) did not confirm the write`, { component: 'KeyManager' });
935
+ logger.warn(`The identity signer store (${signer.name}) did not confirm the write`, {
936
+ component: 'KeyManager',
937
+ });
886
938
  }
887
939
  } catch (error) {
888
- logger.warn(`Failed to write the identity signer store (${signer.name})`, { component: 'KeyManager' }, error);
940
+ logger.warn(
941
+ `Failed to write the identity signer store (${signer.name})`,
942
+ { component: 'KeyManager' },
943
+ error,
944
+ );
889
945
  } finally {
890
946
  KeyManager.invalidateSharedCache();
891
947
  }
@@ -982,7 +1038,7 @@ export class KeyManager {
982
1038
  await store.setItemAsync(STORAGE_KEYS.SHARED_PUBLIC_KEY, publicKey, publicOpts);
983
1039
  } catch (error) {
984
1040
  throw new Error(
985
- `Failed to create shared identity on iOS. Ensure your app has the Keychain Sharing capability enabled with access group "${IOS_KEYCHAIN_GROUP}". Error: ${error}`
1041
+ `Failed to create shared identity on iOS. Ensure your app has the Keychain Sharing capability enabled with access group "${IOS_KEYCHAIN_GROUP}". Error: ${error}`,
986
1042
  );
987
1043
  }
988
1044
  } else if (isAndroid()) {
@@ -1118,11 +1174,15 @@ export class KeyManager {
1118
1174
  return {
1119
1175
  primaryPublicKey,
1120
1176
  sharedPublicKey,
1121
- activePublicKey: isAndroid() ? (primaryPublicKey ?? sharedPublicKey) : (sharedPublicKey ?? primaryPublicKey),
1177
+ activePublicKey: isAndroid()
1178
+ ? (primaryPublicKey ?? sharedPublicKey)
1179
+ : (sharedPublicKey ?? primaryPublicKey),
1122
1180
  // Only a real disagreement is out of sync. A device with just one slot
1123
1181
  // populated has nothing to contradict.
1124
1182
  inSync:
1125
- primaryPublicKey === null || sharedPublicKey === null || primaryPublicKey === sharedPublicKey,
1183
+ primaryPublicKey === null ||
1184
+ sharedPublicKey === null ||
1185
+ primaryPublicKey === sharedPublicKey,
1126
1186
  };
1127
1187
  }
1128
1188
 
@@ -1286,7 +1346,11 @@ export class KeyManager {
1286
1346
  const storage = await KeyManager._advisoryStorage();
1287
1347
  await storage?.setItem(ROTATION_PENDING_KEY, '');
1288
1348
  } catch (error) {
1289
- logger.warn('Failed to clear the pending key rotation marker', { component: 'KeyManager' }, error);
1349
+ logger.warn(
1350
+ 'Failed to clear the pending key rotation marker',
1351
+ { component: 'KeyManager' },
1352
+ error,
1353
+ );
1290
1354
  }
1291
1355
  }
1292
1356
 
@@ -1320,7 +1384,11 @@ export class KeyManager {
1320
1384
  ) {
1321
1385
  return false;
1322
1386
  }
1323
- await KeyManager._persistIdentityAtomic(pair.privateKey, pair.publicKey.toLowerCase(), 'import');
1387
+ await KeyManager._persistIdentityAtomic(
1388
+ pair.privateKey,
1389
+ pair.publicKey.toLowerCase(),
1390
+ 'import',
1391
+ );
1324
1392
  KeyManager.invalidateCache();
1325
1393
  await KeyManager.completeKeyRotation();
1326
1394
  logger.warn(
@@ -1413,7 +1481,9 @@ export class KeyManager {
1413
1481
  priorPrivate = await store.getItemAsync(layout.primaryPrivateKeyName, primaryReadOpts);
1414
1482
  priorPublic = await store.getItemAsync(layout.primaryPublicKeyName, primaryReadOpts);
1415
1483
  } catch (error) {
1416
- logger.error('Failed to read existing primary before persist', error, { component: 'KeyManager' });
1484
+ logger.error('Failed to read existing primary before persist', error, {
1485
+ component: 'KeyManager',
1486
+ });
1417
1487
  throw new IdentityPersistError(
1418
1488
  'Could not read existing identity before writing a new one; refusing to overwrite blind.',
1419
1489
  error,
@@ -1449,11 +1519,24 @@ export class KeyManager {
1449
1519
  KeyManager.canonicalPrivateKey(priorPrivate),
1450
1520
  backupPrivWriteOpts,
1451
1521
  );
1452
- await store.setItemAsync(layout.backupPublicKeyName, priorPublic.toLowerCase(), backupPubWriteOpts);
1453
- await store.setItemAsync(layout.backupTimestampName, Date.now().toString(), backupPubWriteOpts);
1522
+ await store.setItemAsync(
1523
+ layout.backupPublicKeyName,
1524
+ priorPublic.toLowerCase(),
1525
+ backupPubWriteOpts,
1526
+ );
1527
+ await store.setItemAsync(
1528
+ layout.backupTimestampName,
1529
+ Date.now().toString(),
1530
+ backupPubWriteOpts,
1531
+ );
1454
1532
  } catch (error) {
1455
- logger.error('Failed to back up existing identity before overwrite', error, { component: 'KeyManager' });
1456
- throw new IdentityPersistError('Failed to back up existing identity before overwrite', error);
1533
+ logger.error('Failed to back up existing identity before overwrite', error, {
1534
+ component: 'KeyManager',
1535
+ });
1536
+ throw new IdentityPersistError(
1537
+ 'Failed to back up existing identity before overwrite',
1538
+ error,
1539
+ );
1457
1540
  }
1458
1541
  }
1459
1542
  }
@@ -1464,9 +1547,15 @@ export class KeyManager {
1464
1547
  // new primary is proven durable.
1465
1548
  try {
1466
1549
  await store.setItemAsync(layout.primaryPublicKeyName, canonicalPublic, primaryPubWriteOpts);
1467
- await store.setItemAsync(layout.primaryPrivateKeyName, canonicalPrivate, primaryPrivWriteOpts);
1550
+ await store.setItemAsync(
1551
+ layout.primaryPrivateKeyName,
1552
+ canonicalPrivate,
1553
+ primaryPrivWriteOpts,
1554
+ );
1468
1555
  } catch (error) {
1469
- logger.error('Failed to write primary identity to secure store', error, { component: 'KeyManager' });
1556
+ logger.error('Failed to write primary identity to secure store', error, {
1557
+ component: 'KeyManager',
1558
+ });
1470
1559
  await KeyManager._rollbackPrimary(store, layout, priorPrivate, priorPublic);
1471
1560
  throw new IdentityPersistError('Failed to write identity to secure store', error);
1472
1561
  }
@@ -1493,9 +1582,13 @@ export class KeyManager {
1493
1582
  readBackPrivate?.toLowerCase() !== canonicalPrivate ||
1494
1583
  readBackPublic?.toLowerCase() !== canonicalPublic
1495
1584
  ) {
1496
- logger.error('Identity round-trip mismatch after write', undefined, { component: 'KeyManager' });
1585
+ logger.error('Identity round-trip mismatch after write', undefined, {
1586
+ component: 'KeyManager',
1587
+ });
1497
1588
  await KeyManager._rollbackPrimary(store, layout, priorPrivate, priorPublic);
1498
- throw new IdentityPersistError('Identity write was not persisted correctly (round-trip mismatch).');
1589
+ throw new IdentityPersistError(
1590
+ 'Identity write was not persisted correctly (round-trip mismatch).',
1591
+ );
1499
1592
  }
1500
1593
 
1501
1594
  // Final sanity: derive public from the stored private and confirm the
@@ -1535,7 +1628,9 @@ export class KeyManager {
1535
1628
  priorBackupPublic = await store.getItemAsync(layout.backupPublicKeyName, backupReadOpts);
1536
1629
  priorBackupTimestamp = await store.getItemAsync(layout.backupTimestampName, backupReadOpts);
1537
1630
  } catch (error) {
1538
- logger.error('Failed to snapshot identity backup before refresh', error, { component: 'KeyManager' });
1631
+ logger.error('Failed to snapshot identity backup before refresh', error, {
1632
+ component: 'KeyManager',
1633
+ });
1539
1634
  await KeyManager._rollbackPrimary(store, layout, priorPrivate, priorPublic);
1540
1635
  throw new IdentityPersistError('Failed to snapshot identity backup before refresh', error);
1541
1636
  }
@@ -1543,12 +1638,27 @@ export class KeyManager {
1543
1638
  try {
1544
1639
  await store.setItemAsync(layout.backupPrivateKeyName, canonicalPrivate, backupPrivWriteOpts);
1545
1640
  await store.setItemAsync(layout.backupPublicKeyName, canonicalPublic, backupPubWriteOpts);
1546
- await store.setItemAsync(layout.backupTimestampName, Date.now().toString(), backupPubWriteOpts);
1641
+ await store.setItemAsync(
1642
+ layout.backupTimestampName,
1643
+ Date.now().toString(),
1644
+ backupPubWriteOpts,
1645
+ );
1547
1646
  } catch (error) {
1548
- logger.error('Failed to refresh identity backup after primary write', error, { component: 'KeyManager' });
1549
- await KeyManager._rollbackBackup(store, layout, priorBackupPrivate, priorBackupPublic, priorBackupTimestamp);
1647
+ logger.error('Failed to refresh identity backup after primary write', error, {
1648
+ component: 'KeyManager',
1649
+ });
1650
+ await KeyManager._rollbackBackup(
1651
+ store,
1652
+ layout,
1653
+ priorBackupPrivate,
1654
+ priorBackupPublic,
1655
+ priorBackupTimestamp,
1656
+ );
1550
1657
  await KeyManager._rollbackPrimary(store, layout, priorPrivate, priorPublic);
1551
- throw new IdentityPersistError('Failed to refresh identity backup after primary write', error);
1658
+ throw new IdentityPersistError(
1659
+ 'Failed to refresh identity backup after primary write',
1660
+ error,
1661
+ );
1552
1662
  }
1553
1663
 
1554
1664
  // Update cache only after we are certain the identity is durable, then fan
@@ -1603,7 +1713,11 @@ export class KeyManager {
1603
1713
  }
1604
1714
  const sameIdentity = existing?.publicKey === publicKey;
1605
1715
  const keptMnemonic = mnemonic ?? (sameIdentity ? existing?.mnemonic : undefined);
1606
- if (sameIdentity && existing?.privateKey === privateKey && existing?.mnemonic === keptMnemonic) {
1716
+ if (
1717
+ sameIdentity &&
1718
+ existing?.privateKey === privateKey &&
1719
+ existing?.mnemonic === keptMnemonic
1720
+ ) {
1607
1721
  return true; // Already current; do not churn the store.
1608
1722
  }
1609
1723
  await store.write(
@@ -1635,12 +1749,19 @@ export class KeyManager {
1635
1749
  try {
1636
1750
  await store.clear();
1637
1751
  } catch (error) {
1638
- logger.warn(`Failed to clear the identity device backup (${store.name})`, { component: 'KeyManager' }, error);
1752
+ logger.warn(
1753
+ `Failed to clear the identity device backup (${store.name})`,
1754
+ { component: 'KeyManager' },
1755
+ error,
1756
+ );
1639
1757
  }
1640
1758
  }
1641
1759
 
1642
1760
  /** True when `phrase` is a valid BIP-39 phrase that derives `publicKey`. @internal */
1643
- private static async _phraseDerivesPublicKey(phrase: string, publicKey: string): Promise<boolean> {
1761
+ private static async _phraseDerivesPublicKey(
1762
+ phrase: string,
1763
+ publicKey: string,
1764
+ ): Promise<boolean> {
1644
1765
  try {
1645
1766
  // Imported lazily: `./recoveryPhrase` imports this module, and the phrase
1646
1767
  // derivation is frozen there, so it is reused rather than copied.
@@ -1653,10 +1774,17 @@ export class KeyManager {
1653
1774
  }
1654
1775
 
1655
1776
  /** The primary pair when it reads back healthy, else `null`. Never throws. @internal */
1656
- private static async _readHealthyPrimary(): Promise<{ privateKey: string; publicKey: string } | null> {
1777
+ private static async _readHealthyPrimary(): Promise<{
1778
+ privateKey: string;
1779
+ publicKey: string;
1780
+ } | null> {
1657
1781
  try {
1658
1782
  const direct = await KeyManager._readPrimaryDirect();
1659
- if (KeyManager._isHealthyPair(direct.privateKey, direct.publicKey) && direct.privateKey && direct.publicKey) {
1783
+ if (
1784
+ KeyManager._isHealthyPair(direct.privateKey, direct.publicKey) &&
1785
+ direct.privateKey &&
1786
+ direct.publicKey
1787
+ ) {
1660
1788
  return {
1661
1789
  privateKey: KeyManager.canonicalPrivateKey(direct.privateKey),
1662
1790
  publicKey: direct.publicKey.toLowerCase(),
@@ -1734,7 +1862,11 @@ export class KeyManager {
1734
1862
  await writeIdentityMarker({ publicKey, origin });
1735
1863
  }
1736
1864
  } catch (error) {
1737
- logger.warn('Failed to sync identity marker after persist (non-fatal)', { component: 'KeyManager' }, error);
1865
+ logger.warn(
1866
+ 'Failed to sync identity marker after persist (non-fatal)',
1867
+ { component: 'KeyManager' },
1868
+ error,
1869
+ );
1738
1870
  }
1739
1871
  }
1740
1872
 
@@ -1756,24 +1888,46 @@ export class KeyManager {
1756
1888
  const backupReadOpts = KeyManager._slotOpts(layout.backupService);
1757
1889
  try {
1758
1890
  if (priorBackupPrivate) {
1759
- await store.setItemAsync(layout.backupPrivateKeyName, priorBackupPrivate, backupPrivWriteOpts);
1891
+ await store.setItemAsync(
1892
+ layout.backupPrivateKeyName,
1893
+ priorBackupPrivate,
1894
+ backupPrivWriteOpts,
1895
+ );
1760
1896
  } else {
1761
- try { await store.deleteItemAsync(layout.backupPrivateKeyName, backupReadOpts); } catch { /* best effort */ }
1897
+ try {
1898
+ await store.deleteItemAsync(layout.backupPrivateKeyName, backupReadOpts);
1899
+ } catch {
1900
+ /* best effort */
1901
+ }
1762
1902
  }
1763
1903
 
1764
1904
  if (priorBackupPublic) {
1765
1905
  await store.setItemAsync(layout.backupPublicKeyName, priorBackupPublic, backupPubWriteOpts);
1766
1906
  } else {
1767
- try { await store.deleteItemAsync(layout.backupPublicKeyName, backupReadOpts); } catch { /* best effort */ }
1907
+ try {
1908
+ await store.deleteItemAsync(layout.backupPublicKeyName, backupReadOpts);
1909
+ } catch {
1910
+ /* best effort */
1911
+ }
1768
1912
  }
1769
1913
 
1770
1914
  if (priorBackupTimestamp) {
1771
- await store.setItemAsync(layout.backupTimestampName, priorBackupTimestamp, backupPubWriteOpts);
1915
+ await store.setItemAsync(
1916
+ layout.backupTimestampName,
1917
+ priorBackupTimestamp,
1918
+ backupPubWriteOpts,
1919
+ );
1772
1920
  } else {
1773
- try { await store.deleteItemAsync(layout.backupTimestampName, backupReadOpts); } catch { /* best effort */ }
1921
+ try {
1922
+ await store.deleteItemAsync(layout.backupTimestampName, backupReadOpts);
1923
+ } catch {
1924
+ /* best effort */
1925
+ }
1774
1926
  }
1775
1927
  } catch (rollbackError) {
1776
- logger.error('Failed to roll back identity backup after a failed refresh', rollbackError, { component: 'KeyManager' });
1928
+ logger.error('Failed to roll back identity backup after a failed refresh', rollbackError, {
1929
+ component: 'KeyManager',
1930
+ });
1777
1931
  }
1778
1932
  }
1779
1933
 
@@ -1803,11 +1957,21 @@ export class KeyManager {
1803
1957
  } else {
1804
1958
  // There was no prior identity — leave the device empty rather than
1805
1959
  // half-written so hasIdentity() does not lie.
1806
- try { await store.deleteItemAsync(layout.primaryPublicKeyName, primaryReadOpts); } catch { /* best effort */ }
1807
- try { await store.deleteItemAsync(layout.primaryPrivateKeyName, primaryReadOpts); } catch { /* best effort */ }
1960
+ try {
1961
+ await store.deleteItemAsync(layout.primaryPublicKeyName, primaryReadOpts);
1962
+ } catch {
1963
+ /* best effort */
1964
+ }
1965
+ try {
1966
+ await store.deleteItemAsync(layout.primaryPrivateKeyName, primaryReadOpts);
1967
+ } catch {
1968
+ /* best effort */
1969
+ }
1808
1970
  }
1809
1971
  } catch (rollbackError) {
1810
- logger.error('Failed to roll back primary identity after a failed write', rollbackError, { component: 'KeyManager' });
1972
+ logger.error('Failed to roll back primary identity after a failed write', rollbackError, {
1973
+ component: 'KeyManager',
1974
+ });
1811
1975
  } finally {
1812
1976
  // Whatever happened, the cached verdict is no longer trustworthy.
1813
1977
  KeyManager.invalidateCache();
@@ -1825,7 +1989,9 @@ export class KeyManager {
1825
1989
  */
1826
1990
  static async createIdentity(options?: { overwrite?: boolean }): Promise<string> {
1827
1991
  if (isWebPlatform()) {
1828
- throw new Error('Identity creation is only available on native platforms (iOS/Android). Please use the native app to create your identity.');
1992
+ throw new Error(
1993
+ 'Identity creation is only available on native platforms (iOS/Android). Please use the native app to create your identity.',
1994
+ );
1829
1995
  }
1830
1996
 
1831
1997
  // CRITICAL SAFEGUARD: never silently overwrite an existing identity.
@@ -1861,7 +2027,10 @@ export class KeyManager {
1861
2027
  *
1862
2028
  * @internal
1863
2029
  */
1864
- private static async _readPrimaryDirect(): Promise<{ privateKey: string | null; publicKey: string | null }> {
2030
+ private static async _readPrimaryDirect(): Promise<{
2031
+ privateKey: string | null;
2032
+ publicKey: string | null;
2033
+ }> {
1865
2034
  const migration = await KeyManager._ensureIdentitySlotsMigrated();
1866
2035
  if (migration.mode === 'deferred') {
1867
2036
  throw new IdentityUnavailableError(
@@ -1877,7 +2046,10 @@ export class KeyManager {
1877
2046
  const publicKey = await store.getItemAsync(layout.primaryPublicKeyName, readOpts);
1878
2047
  return { privateKey, publicKey };
1879
2048
  } catch (error) {
1880
- throw new IdentityUnavailableError('Could not read existing identity; refusing to write blind.', error);
2049
+ throw new IdentityUnavailableError(
2050
+ 'Could not read existing identity; refusing to write blind.',
2051
+ error,
2052
+ );
1881
2053
  }
1882
2054
  }
1883
2055
 
@@ -1894,7 +2066,9 @@ export class KeyManager {
1894
2066
  options?: { overwrite?: boolean },
1895
2067
  ): Promise<string> {
1896
2068
  if (isWebPlatform()) {
1897
- throw new Error('Identity import is only available on native platforms (iOS/Android). Please use the native app to import your identity.');
2069
+ throw new Error(
2070
+ 'Identity import is only available on native platforms (iOS/Android). Please use the native app to import your identity.',
2071
+ );
1898
2072
  }
1899
2073
 
1900
2074
  if (!KeyManager.isValidPrivateKey(privateKey)) {
@@ -2061,7 +2235,11 @@ export class KeyManager {
2061
2235
  if (KeyManager.deviceBackupStore) {
2062
2236
  const primary = await KeyManager._readHealthyPrimary();
2063
2237
  if (primary && (await KeyManager._phraseDerivesPublicKey(mnemonic, primary.publicKey))) {
2064
- await KeyManager._writeDeviceBackup(primary.privateKey, primary.publicKey, mnemonic.trim().toLowerCase());
2238
+ await KeyManager._writeDeviceBackup(
2239
+ primary.privateKey,
2240
+ primary.publicKey,
2241
+ mnemonic.trim().toLowerCase(),
2242
+ );
2065
2243
  }
2066
2244
  }
2067
2245
  }
@@ -2091,7 +2269,10 @@ export class KeyManager {
2091
2269
  if (isDev()) {
2092
2270
  logger.warn('Failed to read recovery mnemonic', { component: 'KeyManager' }, error);
2093
2271
  }
2094
- throw new IdentityUnavailableError('Failed to read recovery mnemonic from secure storage.', error);
2272
+ throw new IdentityUnavailableError(
2273
+ 'Failed to read recovery mnemonic from secure storage.',
2274
+ error,
2275
+ );
2095
2276
  }
2096
2277
  }
2097
2278
 
@@ -2105,7 +2286,11 @@ export class KeyManager {
2105
2286
  return; // Identity storage is only available on native platforms
2106
2287
  }
2107
2288
  const store = await initSecureStore();
2108
- await KeyManager._bestEffortDelete(store, RECOVERY_MNEMONIC_STORAGE_KEY, RECOVERY_MNEMONIC_KEYCHAIN_SERVICE);
2289
+ await KeyManager._bestEffortDelete(
2290
+ store,
2291
+ RECOVERY_MNEMONIC_STORAGE_KEY,
2292
+ RECOVERY_MNEMONIC_KEYCHAIN_SERVICE,
2293
+ );
2109
2294
  }
2110
2295
 
2111
2296
  /**
@@ -2132,7 +2317,10 @@ export class KeyManager {
2132
2317
 
2133
2318
  const migration = await KeyManager._ensureIdentitySlotsMigrated();
2134
2319
  if (migration.mode === 'deferred') {
2135
- throw new IdentityUnavailableError('Identity storage is temporarily unavailable.', migration.cause);
2320
+ throw new IdentityUnavailableError(
2321
+ 'Identity storage is temporarily unavailable.',
2322
+ migration.cause,
2323
+ );
2136
2324
  }
2137
2325
 
2138
2326
  let privateKey: string | null;
@@ -2140,15 +2328,23 @@ export class KeyManager {
2140
2328
  try {
2141
2329
  const store = await initSecureStore();
2142
2330
  [privateKey, publicKey] = await Promise.all([
2143
- store.getItemAsync(migration.layout.primaryPrivateKeyName, KeyManager._slotOpts(migration.layout.primaryService)),
2144
- store.getItemAsync(migration.layout.primaryPublicKeyName, KeyManager._slotOpts(migration.layout.primaryService)),
2331
+ store.getItemAsync(
2332
+ migration.layout.primaryPrivateKeyName,
2333
+ KeyManager._slotOpts(migration.layout.primaryService),
2334
+ ),
2335
+ store.getItemAsync(
2336
+ migration.layout.primaryPublicKeyName,
2337
+ KeyManager._slotOpts(migration.layout.primaryService),
2338
+ ),
2145
2339
  ]);
2146
2340
  } catch (error) {
2147
2341
  // Storage threw — could be a transient keychain lock (e.g., background
2148
2342
  // fetch before the device is unlocked). Do NOT cache; throw a TYPED error
2149
2343
  // so callers distinguish "temporarily unavailable" from "genuinely absent"
2150
2344
  // instead of silently treating a locked keystore as a blank device.
2151
- logger.error('Failed to read identity from secure storage', error, { component: 'KeyManager' });
2345
+ logger.error('Failed to read identity from secure storage', error, {
2346
+ component: 'KeyManager',
2347
+ });
2152
2348
  throw new IdentityUnavailableError('Failed to read identity from secure storage.', error);
2153
2349
  }
2154
2350
 
@@ -2163,9 +2359,7 @@ export class KeyManager {
2163
2359
  // sign-in flow when SignatureService.sign() can't find the keypair.
2164
2360
  if (KeyManager.isValidPrivateKey(privateKey) && KeyManager.isValidPublicKey(publicKey)) {
2165
2361
  try {
2166
- const derived = deriveSecp256k1PublicKey(
2167
- KeyManager.canonicalPrivateKey(privateKey),
2168
- );
2362
+ const derived = deriveSecp256k1PublicKey(KeyManager.canonicalPrivateKey(privateKey));
2169
2363
  // Hex equality is case-insensitive; normalize on both sides to
2170
2364
  // tolerate legacy uppercase-stored public keys.
2171
2365
  hasIdentity = derived.toLowerCase() === publicKey.toLowerCase();
@@ -2183,10 +2377,9 @@ export class KeyManager {
2183
2377
  );
2184
2378
  }
2185
2379
  } else {
2186
- logger.warn(
2187
- 'KeyManager.hasIdentity: stored key material is malformed',
2188
- { component: 'KeyManager' },
2189
- );
2380
+ logger.warn('KeyManager.hasIdentity: stored key material is malformed', {
2381
+ component: 'KeyManager',
2382
+ });
2190
2383
  }
2191
2384
  }
2192
2385
 
@@ -2300,16 +2493,18 @@ export class KeyManager {
2300
2493
  * @param userConfirmed - If true, user has explicitly confirmed deletion (default: false)
2301
2494
  */
2302
2495
  static async deleteIdentity(
2303
- skipBackup = false,
2496
+ skipBackup = false,
2304
2497
  force = false,
2305
- userConfirmed = false
2498
+ userConfirmed = false,
2306
2499
  ): Promise<void> {
2307
2500
  if (isWebPlatform()) {
2308
2501
  return; // Identity storage is only available on native platforms, nothing to delete
2309
2502
  }
2310
2503
  // CRITICAL SAFEGUARD: Require explicit user confirmation unless force is true
2311
2504
  if (!force && !userConfirmed) {
2312
- throw new Error('Identity deletion requires explicit user confirmation. This is a safety measure to prevent accidental data loss.');
2505
+ throw new Error(
2506
+ 'Identity deletion requires explicit user confirmation. This is a safety measure to prevent accidental data loss.',
2507
+ );
2313
2508
  }
2314
2509
 
2315
2510
  if (!force) {
@@ -2328,11 +2523,17 @@ export class KeyManager {
2328
2523
  try {
2329
2524
  const backupSuccess = await KeyManager.backupIdentity();
2330
2525
  if (!backupSuccess && isDev()) {
2331
- logger.warn('Failed to backup identity before deletion - proceeding anyway', { component: 'KeyManager' });
2526
+ logger.warn('Failed to backup identity before deletion - proceeding anyway', {
2527
+ component: 'KeyManager',
2528
+ });
2332
2529
  }
2333
2530
  } catch (backupError) {
2334
2531
  if (isDev()) {
2335
- logger.warn('Failed to backup identity before deletion', { component: 'KeyManager' }, backupError);
2532
+ logger.warn(
2533
+ 'Failed to backup identity before deletion',
2534
+ { component: 'KeyManager' },
2535
+ backupError,
2536
+ );
2336
2537
  }
2337
2538
  }
2338
2539
  }
@@ -2378,7 +2579,11 @@ export class KeyManager {
2378
2579
  try {
2379
2580
  await clearIdentityMarker();
2380
2581
  } catch (error) {
2381
- logger.warn('Failed to clear identity marker during delete', { component: 'KeyManager' }, error);
2582
+ logger.warn(
2583
+ 'Failed to clear identity marker during delete',
2584
+ { component: 'KeyManager' },
2585
+ error,
2586
+ );
2382
2587
  }
2383
2588
 
2384
2589
  // Invalidate cache LAST — its subscriber fan-out fires only after both the
@@ -2418,8 +2623,16 @@ export class KeyManager {
2418
2623
  privateKey,
2419
2624
  KeyManager._privateWriteOpts(store, layout.backupService),
2420
2625
  );
2421
- await store.setItemAsync(layout.backupPublicKeyName, publicKey, KeyManager._slotOpts(layout.backupService));
2422
- await store.setItemAsync(layout.backupTimestampName, Date.now().toString(), KeyManager._slotOpts(layout.backupService));
2626
+ await store.setItemAsync(
2627
+ layout.backupPublicKeyName,
2628
+ publicKey,
2629
+ KeyManager._slotOpts(layout.backupService),
2630
+ );
2631
+ await store.setItemAsync(
2632
+ layout.backupTimestampName,
2633
+ Date.now().toString(),
2634
+ KeyManager._slotOpts(layout.backupService),
2635
+ );
2423
2636
 
2424
2637
  return true;
2425
2638
  } catch (error) {
@@ -2473,7 +2686,9 @@ export class KeyManager {
2473
2686
  const probeHash = '0'.repeat(64);
2474
2687
  const signature = signSecp256k1Digest(canonicalPrivateKey, probeHash);
2475
2688
  if (!verifySecp256k1Digest(publicKey, probeHash, signature)) {
2476
- logger.error('Identity sign/verify probe failed during integrity check', undefined, { component: 'KeyManager' });
2689
+ logger.error('Identity sign/verify probe failed during integrity check', undefined, {
2690
+ component: 'KeyManager',
2691
+ });
2477
2692
  return false;
2478
2693
  }
2479
2694
 
@@ -2554,15 +2769,23 @@ export class KeyManager {
2554
2769
  }
2555
2770
 
2556
2771
  // Load + validate the backup.
2557
- const backupPrivateKey = await store.getItemAsync(layout.backupPrivateKeyName, backupReadOpts);
2772
+ const backupPrivateKey = await store.getItemAsync(
2773
+ layout.backupPrivateKeyName,
2774
+ backupReadOpts,
2775
+ );
2558
2776
  const backupPublicKey = await store.getItemAsync(layout.backupPublicKeyName, backupReadOpts);
2559
2777
 
2560
2778
  if (!backupPrivateKey || !backupPublicKey) {
2561
2779
  return false; // No backup available
2562
2780
  }
2563
2781
 
2564
- if (!KeyManager.isValidPrivateKey(backupPrivateKey) || !KeyManager.isValidPublicKey(backupPublicKey)) {
2565
- logger.warn('Backup identity is malformed; refusing to restore', { component: 'KeyManager' });
2782
+ if (
2783
+ !KeyManager.isValidPrivateKey(backupPrivateKey) ||
2784
+ !KeyManager.isValidPublicKey(backupPublicKey)
2785
+ ) {
2786
+ logger.warn('Backup identity is malformed; refusing to restore', {
2787
+ component: 'KeyManager',
2788
+ });
2566
2789
  return false;
2567
2790
  }
2568
2791
 
@@ -2571,7 +2794,9 @@ export class KeyManager {
2571
2794
  // be considered valid.
2572
2795
  const derivedPublicKey = KeyManager.derivePublicKey(backupPrivateKey);
2573
2796
  if (derivedPublicKey.toLowerCase() !== backupPublicKey.toLowerCase()) {
2574
- logger.warn('Backup public key does not match derived; refusing to restore', { component: 'KeyManager' });
2797
+ logger.warn('Backup public key does not match derived; refusing to restore', {
2798
+ component: 'KeyManager',
2799
+ });
2575
2800
  return false;
2576
2801
  }
2577
2802
 
@@ -2581,10 +2806,7 @@ export class KeyManager {
2581
2806
  // the user. We check the private key too (not just the public): a
2582
2807
  // present private key that derives to a non-backup public means a real,
2583
2808
  // different identity is sitting in the primary slot.
2584
- if (
2585
- primaryPublic &&
2586
- primaryPublic.toLowerCase() !== backupPublicKey.toLowerCase()
2587
- ) {
2809
+ if (primaryPublic && primaryPublic.toLowerCase() !== backupPublicKey.toLowerCase()) {
2588
2810
  logger.error(
2589
2811
  'Primary public key is present, corrupt-or-mismatched, AND differs from the backup. Refusing to restore to avoid switching accounts.',
2590
2812
  undefined,
@@ -2610,7 +2832,9 @@ export class KeyManager {
2610
2832
  try {
2611
2833
  await KeyManager._persistIdentityAtomic(backupPrivateKey, backupPublicKey, 'restore');
2612
2834
  } catch (error) {
2613
- logger.error('Failed to persist identity restored from backup', error, { component: 'KeyManager' });
2835
+ logger.error('Failed to persist identity restored from backup', error, {
2836
+ component: 'KeyManager',
2837
+ });
2614
2838
  return false;
2615
2839
  }
2616
2840
 
@@ -2685,7 +2909,9 @@ export class KeyManager {
2685
2909
  const backupCandidate = await KeyManager._readBackupCandidate();
2686
2910
  if (backupCandidate) {
2687
2911
  if (backupCandidate.publicKey.toLowerCase() === expectedPublic) {
2688
- if (await KeyManager._commitRecovery(backupCandidate.privateKey, backupCandidate.publicKey)) {
2912
+ if (
2913
+ await KeyManager._commitRecovery(backupCandidate.privateKey, backupCandidate.publicKey)
2914
+ ) {
2689
2915
  return { recovered: true, source: 'backup', publicKey: backupCandidate.publicKey };
2690
2916
  }
2691
2917
  } else {
@@ -2697,7 +2923,9 @@ export class KeyManager {
2697
2923
  const sharedCandidate = await KeyManager._readSharedCandidate();
2698
2924
  if (sharedCandidate) {
2699
2925
  if (sharedCandidate.publicKey.toLowerCase() === expectedPublic) {
2700
- if (await KeyManager._commitRecovery(sharedCandidate.privateKey, sharedCandidate.publicKey)) {
2926
+ if (
2927
+ await KeyManager._commitRecovery(sharedCandidate.privateKey, sharedCandidate.publicKey)
2928
+ ) {
2701
2929
  return { recovered: true, source: 'shared', publicKey: sharedCandidate.publicKey };
2702
2930
  }
2703
2931
  } else {
@@ -2739,7 +2967,11 @@ export class KeyManager {
2739
2967
  publicKey: record.publicKey.toLowerCase(),
2740
2968
  };
2741
2969
  } catch (error) {
2742
- logger.warn(`Recovery: failed to read the device backup (${store.name})`, { component: 'KeyManager' }, error);
2970
+ logger.warn(
2971
+ `Recovery: failed to read the device backup (${store.name})`,
2972
+ { component: 'KeyManager' },
2973
+ error,
2974
+ );
2743
2975
  return null;
2744
2976
  }
2745
2977
  }
@@ -2753,18 +2985,28 @@ export class KeyManager {
2753
2985
  if (!(await KeyManager._commitRecovery(record.privateKey, record.publicKey))) {
2754
2986
  return false;
2755
2987
  }
2756
- if (record.mnemonic && (await KeyManager._phraseDerivesPublicKey(record.mnemonic, record.publicKey))) {
2988
+ if (
2989
+ record.mnemonic &&
2990
+ (await KeyManager._phraseDerivesPublicKey(record.mnemonic, record.publicKey))
2991
+ ) {
2757
2992
  try {
2758
2993
  await KeyManager.storeRecoveryMnemonic(record.mnemonic);
2759
2994
  } catch (error) {
2760
- logger.warn('Recovery: restored the identity but not its phrase slot', { component: 'KeyManager' }, error);
2995
+ logger.warn(
2996
+ 'Recovery: restored the identity but not its phrase slot',
2997
+ { component: 'KeyManager' },
2998
+ error,
2999
+ );
2761
3000
  }
2762
3001
  }
2763
3002
  return true;
2764
3003
  }
2765
3004
 
2766
3005
  /** Read the active-layout backup slot as a healthy candidate, or null. @internal */
2767
- private static async _readBackupCandidate(): Promise<{ privateKey: string; publicKey: string } | null> {
3006
+ private static async _readBackupCandidate(): Promise<{
3007
+ privateKey: string;
3008
+ publicKey: string;
3009
+ } | null> {
2768
3010
  try {
2769
3011
  const migration = await KeyManager._ensureIdentitySlotsMigrated();
2770
3012
  if (migration.mode === 'deferred') {
@@ -2790,7 +3032,10 @@ export class KeyManager {
2790
3032
  * keychain group, or on Android Commons' own signer store (read in-process;
2791
3033
  * Commons never gets it back over IPC). @internal
2792
3034
  */
2793
- private static async _readSharedCandidate(): Promise<{ privateKey: string; publicKey: string } | null> {
3035
+ private static async _readSharedCandidate(): Promise<{
3036
+ privateKey: string;
3037
+ publicKey: string;
3038
+ } | null> {
2794
3039
  try {
2795
3040
  let privateKey: string | null = null;
2796
3041
  let publicKey: string | null = null;
@@ -2819,7 +3064,9 @@ export class KeyManager {
2819
3064
  KeyManager.invalidateCache();
2820
3065
  return true;
2821
3066
  } catch (error) {
2822
- logger.error('Recovery: failed to persist recovered identity', error, { component: 'KeyManager' });
3067
+ logger.error('Recovery: failed to persist recovered identity', error, {
3068
+ component: 'KeyManager',
3069
+ });
2823
3070
  return false;
2824
3071
  }
2825
3072
  }
@@ -2966,7 +3213,10 @@ export class KeyManager {
2966
3213
  * @returns the signature, or `null` on web / when no identity key is
2967
3214
  * available to this app.
2968
3215
  */
2969
- static async signSocialReceive(index: number, digest: string): Promise<CommonsSocialReceiveSignature | null> {
3216
+ static async signSocialReceive(
3217
+ index: number,
3218
+ digest: string,
3219
+ ): Promise<CommonsSocialReceiveSignature | null> {
2970
3220
  if (isWebPlatform()) {
2971
3221
  return null;
2972
3222
  }