@oxy.so/core 1.0.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 (673) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/README.md +221 -0
  4. package/dist/cjs/.tsbuildinfo +1 -0
  5. package/dist/cjs/HttpService.js +1270 -0
  6. package/dist/cjs/OxyServices.base.js +390 -0
  7. package/dist/cjs/OxyServices.errors.js +95 -0
  8. package/dist/cjs/OxyServices.js +72 -0
  9. package/dist/cjs/boot/sessionColdBoot.js +395 -0
  10. package/dist/cjs/constants/version.js +16 -0
  11. package/dist/cjs/crypto/aead.js +79 -0
  12. package/dist/cjs/crypto/ecdh.js +35 -0
  13. package/dist/cjs/crypto/identityMarker.js +255 -0
  14. package/dist/cjs/crypto/kdf.js +39 -0
  15. package/dist/cjs/crypto/keyManager.js +2234 -0
  16. package/dist/cjs/crypto/polyfill.js +131 -0
  17. package/dist/cjs/crypto/recoveryPhrase.js +242 -0
  18. package/dist/cjs/crypto/signatureService.js +273 -0
  19. package/dist/cjs/i18n/accountCategoryLabels.js +44 -0
  20. package/dist/cjs/i18n/accountRoleLabels.js +27 -0
  21. package/dist/cjs/i18n/index.js +90 -0
  22. package/dist/cjs/i18n/locales/ar-SA.json +282 -0
  23. package/dist/cjs/i18n/locales/ca-ES.json +282 -0
  24. package/dist/cjs/i18n/locales/de-DE.json +282 -0
  25. package/dist/cjs/i18n/locales/en-US.json +2161 -0
  26. package/dist/cjs/i18n/locales/es-ES.json +2161 -0
  27. package/dist/cjs/i18n/locales/fr-FR.json +282 -0
  28. package/dist/cjs/i18n/locales/it-IT.json +282 -0
  29. package/dist/cjs/i18n/locales/ja-JP.json +282 -0
  30. package/dist/cjs/i18n/locales/ko-KR.json +282 -0
  31. package/dist/cjs/i18n/locales/locales/ar-SA.json +282 -0
  32. package/dist/cjs/i18n/locales/locales/ca-ES.json +282 -0
  33. package/dist/cjs/i18n/locales/locales/de-DE.json +282 -0
  34. package/dist/cjs/i18n/locales/locales/en-US.json +2161 -0
  35. package/dist/cjs/i18n/locales/locales/es-ES.json +2161 -0
  36. package/dist/cjs/i18n/locales/locales/fr-FR.json +282 -0
  37. package/dist/cjs/i18n/locales/locales/it-IT.json +282 -0
  38. package/dist/cjs/i18n/locales/locales/ja-JP.json +282 -0
  39. package/dist/cjs/i18n/locales/locales/ko-KR.json +282 -0
  40. package/dist/cjs/i18n/locales/locales/pt-PT.json +282 -0
  41. package/dist/cjs/i18n/locales/locales/zh-CN.json +282 -0
  42. package/dist/cjs/i18n/locales/pt-PT.json +282 -0
  43. package/dist/cjs/i18n/locales/zh-CN.json +282 -0
  44. package/dist/cjs/i18n/reputationCategoryLabels.js +20 -0
  45. package/dist/cjs/i18n/trustTierLabels.js +19 -0
  46. package/dist/cjs/index.js +498 -0
  47. package/dist/cjs/inference/OxyInferenceClient.js +544 -0
  48. package/dist/cjs/logger/index.js +149 -0
  49. package/dist/cjs/mixins/OxyServices.accounts.js +555 -0
  50. package/dist/cjs/mixins/OxyServices.analytics.js +49 -0
  51. package/dist/cjs/mixins/OxyServices.appData.js +108 -0
  52. package/dist/cjs/mixins/OxyServices.assets.js +764 -0
  53. package/dist/cjs/mixins/OxyServices.auth.js +1254 -0
  54. package/dist/cjs/mixins/OxyServices.chains.js +73 -0
  55. package/dist/cjs/mixins/OxyServices.civic.js +611 -0
  56. package/dist/cjs/mixins/OxyServices.connectedApps.js +78 -0
  57. package/dist/cjs/mixins/OxyServices.contacts.js +50 -0
  58. package/dist/cjs/mixins/OxyServices.deviceBoot.js +191 -0
  59. package/dist/cjs/mixins/OxyServices.deviceTransfer.js +314 -0
  60. package/dist/cjs/mixins/OxyServices.devices.js +116 -0
  61. package/dist/cjs/mixins/OxyServices.features.js +335 -0
  62. package/dist/cjs/mixins/OxyServices.followGraph.js +221 -0
  63. package/dist/cjs/mixins/OxyServices.identity.js +461 -0
  64. package/dist/cjs/mixins/OxyServices.identityBackup.js +172 -0
  65. package/dist/cjs/mixins/OxyServices.inference.js +59 -0
  66. package/dist/cjs/mixins/OxyServices.language.js +125 -0
  67. package/dist/cjs/mixins/OxyServices.links.js +72 -0
  68. package/dist/cjs/mixins/OxyServices.location.js +43 -0
  69. package/dist/cjs/mixins/OxyServices.nodes.js +175 -0
  70. package/dist/cjs/mixins/OxyServices.notifications.js +63 -0
  71. package/dist/cjs/mixins/OxyServices.payment.js +158 -0
  72. package/dist/cjs/mixins/OxyServices.privacy.js +199 -0
  73. package/dist/cjs/mixins/OxyServices.reputation.js +289 -0
  74. package/dist/cjs/mixins/OxyServices.security.js +89 -0
  75. package/dist/cjs/mixins/OxyServices.store.js +266 -0
  76. package/dist/cjs/mixins/OxyServices.topics.js +137 -0
  77. package/dist/cjs/mixins/OxyServices.user.js +902 -0
  78. package/dist/cjs/mixins/OxyServices.utility.js +1098 -0
  79. package/dist/cjs/mixins/index.js +135 -0
  80. package/dist/cjs/mixins/mixinHelpers.js +53 -0
  81. package/dist/cjs/models/Topic.js +16 -0
  82. package/dist/cjs/models/interfaces.js +20 -0
  83. package/dist/cjs/models/session.js +2 -0
  84. package/dist/cjs/server/auth.js +167 -0
  85. package/dist/cjs/server/capabilityTicket.js +192 -0
  86. package/dist/cjs/server/cors.js +225 -0
  87. package/dist/cjs/server/index.js +97 -0
  88. package/dist/cjs/server/rateLimit.js +207 -0
  89. package/dist/cjs/server/safeFetch.js +458 -0
  90. package/dist/cjs/server/securityHeaders.js +377 -0
  91. package/dist/cjs/server/userInvalidation.js +156 -0
  92. package/dist/cjs/server/verifySecret.js +50 -0
  93. package/dist/cjs/session/SessionClient.js +865 -0
  94. package/dist/cjs/session/accountDelegationAccess.js +48 -0
  95. package/dist/cjs/session/accountDialogController.js +1131 -0
  96. package/dist/cjs/session/accountSwitchTargets.js +81 -0
  97. package/dist/cjs/session/authStateStore.js +404 -0
  98. package/dist/cjs/session/createSessionClient.js +32 -0
  99. package/dist/cjs/session/deviceDirectory.js +143 -0
  100. package/dist/cjs/session/deviceSwitcherRows.js +76 -0
  101. package/dist/cjs/session/identityPin.js +256 -0
  102. package/dist/cjs/session/identitySession.js +135 -0
  103. package/dist/cjs/session/projectSessionState.js +121 -0
  104. package/dist/cjs/session/refresh.js +398 -0
  105. package/dist/cjs/session/sessionClientHost.js +34 -0
  106. package/dist/cjs/session/sharedDeviceCredential.js +247 -0
  107. package/dist/cjs/session/socketLoader.js +55 -0
  108. package/dist/cjs/shared/utils/colorUtils.js +153 -0
  109. package/dist/cjs/shared/utils/errorUtils.js +183 -0
  110. package/dist/cjs/shared/utils/index.js +49 -0
  111. package/dist/cjs/shared/utils/networkUtils.js +183 -0
  112. package/dist/cjs/shared/utils/themeUtils.js +106 -0
  113. package/dist/cjs/utils/accountCacheSweep.js +80 -0
  114. package/dist/cjs/utils/accountUtils.js +140 -0
  115. package/dist/cjs/utils/activityId.js +6 -0
  116. package/dist/cjs/utils/apiUtils.js +91 -0
  117. package/dist/cjs/utils/asyncUtils.js +222 -0
  118. package/dist/cjs/utils/authHelpers.js +120 -0
  119. package/dist/cjs/utils/authWebUrl.js +19 -0
  120. package/dist/cjs/utils/avatarUtils.js +29 -0
  121. package/dist/cjs/utils/cache.js +235 -0
  122. package/dist/cjs/utils/cacheKey.js +85 -0
  123. package/dist/cjs/utils/coldBoot.js +122 -0
  124. package/dist/cjs/utils/commonsApproval.js +31 -0
  125. package/dist/cjs/utils/commonsDelivery.js +59 -0
  126. package/dist/cjs/utils/deviceManager.js +174 -0
  127. package/dist/cjs/utils/displayNamePolicyRanges.generated.js +60 -0
  128. package/dist/cjs/utils/edgeRegion.js +6 -0
  129. package/dist/cjs/utils/errorUtils.js +243 -0
  130. package/dist/cjs/utils/identityCacheSweep.js +97 -0
  131. package/dist/cjs/utils/languageUtils.js +224 -0
  132. package/dist/cjs/utils/oauthPkce.js +311 -0
  133. package/dist/cjs/utils/officialOrigins.js +64 -0
  134. package/dist/cjs/utils/oxyServiceEnvironment.js +19 -0
  135. package/dist/cjs/utils/platform.js +119 -0
  136. package/dist/cjs/utils/profileLinks.js +63 -0
  137. package/dist/cjs/utils/redactUrl.js +29 -0
  138. package/dist/cjs/utils/registrableApex.js +45 -0
  139. package/dist/cjs/utils/requestUtils.js +209 -0
  140. package/dist/cjs/utils/sessionUtils.js +181 -0
  141. package/dist/cjs/utils/textNormalization.js +158 -0
  142. package/dist/cjs/utils/userHandle.js +37 -0
  143. package/dist/cjs/utils/userIdentity.js +50 -0
  144. package/dist/cjs/utils/validationUtils.js +370 -0
  145. package/dist/cjs/utils/webauthnOrigin.js +51 -0
  146. package/dist/esm/.tsbuildinfo +1 -0
  147. package/dist/esm/HttpService.js +1266 -0
  148. package/dist/esm/OxyServices.base.js +386 -0
  149. package/dist/esm/OxyServices.errors.js +88 -0
  150. package/dist/esm/OxyServices.js +66 -0
  151. package/dist/esm/boot/sessionColdBoot.js +392 -0
  152. package/dist/esm/constants/version.js +13 -0
  153. package/dist/esm/crypto/aead.js +74 -0
  154. package/dist/esm/crypto/ecdh.js +32 -0
  155. package/dist/esm/crypto/identityMarker.js +248 -0
  156. package/dist/esm/crypto/kdf.js +36 -0
  157. package/dist/esm/crypto/keyManager.js +2227 -0
  158. package/dist/esm/crypto/polyfill.js +129 -0
  159. package/dist/esm/crypto/recoveryPhrase.js +238 -0
  160. package/dist/esm/crypto/signatureService.js +269 -0
  161. package/dist/esm/i18n/accountCategoryLabels.js +37 -0
  162. package/dist/esm/i18n/accountRoleLabels.js +20 -0
  163. package/dist/esm/i18n/index.js +83 -0
  164. package/dist/esm/i18n/locales/ar-SA.json +282 -0
  165. package/dist/esm/i18n/locales/ca-ES.json +282 -0
  166. package/dist/esm/i18n/locales/de-DE.json +282 -0
  167. package/dist/esm/i18n/locales/en-US.json +2161 -0
  168. package/dist/esm/i18n/locales/es-ES.json +2161 -0
  169. package/dist/esm/i18n/locales/fr-FR.json +282 -0
  170. package/dist/esm/i18n/locales/it-IT.json +282 -0
  171. package/dist/esm/i18n/locales/ja-JP.json +282 -0
  172. package/dist/esm/i18n/locales/ko-KR.json +282 -0
  173. package/dist/esm/i18n/locales/locales/ar-SA.json +282 -0
  174. package/dist/esm/i18n/locales/locales/ca-ES.json +282 -0
  175. package/dist/esm/i18n/locales/locales/de-DE.json +282 -0
  176. package/dist/esm/i18n/locales/locales/en-US.json +2161 -0
  177. package/dist/esm/i18n/locales/locales/es-ES.json +2161 -0
  178. package/dist/esm/i18n/locales/locales/fr-FR.json +282 -0
  179. package/dist/esm/i18n/locales/locales/it-IT.json +282 -0
  180. package/dist/esm/i18n/locales/locales/ja-JP.json +282 -0
  181. package/dist/esm/i18n/locales/locales/ko-KR.json +282 -0
  182. package/dist/esm/i18n/locales/locales/pt-PT.json +282 -0
  183. package/dist/esm/i18n/locales/locales/zh-CN.json +282 -0
  184. package/dist/esm/i18n/locales/pt-PT.json +282 -0
  185. package/dist/esm/i18n/locales/zh-CN.json +282 -0
  186. package/dist/esm/i18n/reputationCategoryLabels.js +13 -0
  187. package/dist/esm/i18n/trustTierLabels.js +12 -0
  188. package/dist/esm/index.js +254 -0
  189. package/dist/esm/inference/OxyInferenceClient.js +538 -0
  190. package/dist/esm/logger/index.js +140 -0
  191. package/dist/esm/mixins/OxyServices.accounts.js +543 -0
  192. package/dist/esm/mixins/OxyServices.analytics.js +46 -0
  193. package/dist/esm/mixins/OxyServices.appData.js +103 -0
  194. package/dist/esm/mixins/OxyServices.assets.js +761 -0
  195. package/dist/esm/mixins/OxyServices.auth.js +1247 -0
  196. package/dist/esm/mixins/OxyServices.chains.js +70 -0
  197. package/dist/esm/mixins/OxyServices.civic.js +605 -0
  198. package/dist/esm/mixins/OxyServices.connectedApps.js +75 -0
  199. package/dist/esm/mixins/OxyServices.contacts.js +47 -0
  200. package/dist/esm/mixins/OxyServices.deviceBoot.js +186 -0
  201. package/dist/esm/mixins/OxyServices.deviceTransfer.js +311 -0
  202. package/dist/esm/mixins/OxyServices.devices.js +113 -0
  203. package/dist/esm/mixins/OxyServices.features.js +332 -0
  204. package/dist/esm/mixins/OxyServices.followGraph.js +218 -0
  205. package/dist/esm/mixins/OxyServices.identity.js +457 -0
  206. package/dist/esm/mixins/OxyServices.identityBackup.js +169 -0
  207. package/dist/esm/mixins/OxyServices.inference.js +56 -0
  208. package/dist/esm/mixins/OxyServices.language.js +122 -0
  209. package/dist/esm/mixins/OxyServices.links.js +69 -0
  210. package/dist/esm/mixins/OxyServices.location.js +40 -0
  211. package/dist/esm/mixins/OxyServices.nodes.js +172 -0
  212. package/dist/esm/mixins/OxyServices.notifications.js +60 -0
  213. package/dist/esm/mixins/OxyServices.payment.js +155 -0
  214. package/dist/esm/mixins/OxyServices.privacy.js +196 -0
  215. package/dist/esm/mixins/OxyServices.reputation.js +286 -0
  216. package/dist/esm/mixins/OxyServices.security.js +86 -0
  217. package/dist/esm/mixins/OxyServices.store.js +263 -0
  218. package/dist/esm/mixins/OxyServices.topics.js +134 -0
  219. package/dist/esm/mixins/OxyServices.user.js +899 -0
  220. package/dist/esm/mixins/OxyServices.utility.js +1095 -0
  221. package/dist/esm/mixins/index.js +132 -0
  222. package/dist/esm/mixins/mixinHelpers.js +48 -0
  223. package/dist/esm/models/Topic.js +13 -0
  224. package/dist/esm/models/interfaces.js +17 -0
  225. package/dist/esm/models/session.js +1 -0
  226. package/dist/esm/server/auth.js +154 -0
  227. package/dist/esm/server/capabilityTicket.js +183 -0
  228. package/dist/esm/server/cors.js +220 -0
  229. package/dist/esm/server/index.js +47 -0
  230. package/dist/esm/server/rateLimit.js +201 -0
  231. package/dist/esm/server/safeFetch.js +447 -0
  232. package/dist/esm/server/securityHeaders.js +364 -0
  233. package/dist/esm/server/userInvalidation.js +152 -0
  234. package/dist/esm/server/verifySecret.js +47 -0
  235. package/dist/esm/session/SessionClient.js +861 -0
  236. package/dist/esm/session/accountDelegationAccess.js +45 -0
  237. package/dist/esm/session/accountDialogController.js +1126 -0
  238. package/dist/esm/session/accountSwitchTargets.js +77 -0
  239. package/dist/esm/session/authStateStore.js +398 -0
  240. package/dist/esm/session/createSessionClient.js +29 -0
  241. package/dist/esm/session/deviceDirectory.js +135 -0
  242. package/dist/esm/session/deviceSwitcherRows.js +72 -0
  243. package/dist/esm/session/identityPin.js +249 -0
  244. package/dist/esm/session/identitySession.js +131 -0
  245. package/dist/esm/session/projectSessionState.js +114 -0
  246. package/dist/esm/session/refresh.js +390 -0
  247. package/dist/esm/session/sessionClientHost.js +31 -0
  248. package/dist/esm/session/sharedDeviceCredential.js +239 -0
  249. package/dist/esm/session/socketLoader.js +19 -0
  250. package/dist/esm/shared/utils/colorUtils.js +143 -0
  251. package/dist/esm/shared/utils/errorUtils.js +170 -0
  252. package/dist/esm/shared/utils/index.js +15 -0
  253. package/dist/esm/shared/utils/networkUtils.js +173 -0
  254. package/dist/esm/shared/utils/themeUtils.js +98 -0
  255. package/dist/esm/utils/accountCacheSweep.js +75 -0
  256. package/dist/esm/utils/accountUtils.js +131 -0
  257. package/dist/esm/utils/activityId.js +2 -0
  258. package/dist/esm/utils/apiUtils.js +84 -0
  259. package/dist/esm/utils/asyncUtils.js +207 -0
  260. package/dist/esm/utils/authHelpers.js +111 -0
  261. package/dist/esm/utils/authWebUrl.js +16 -0
  262. package/dist/esm/utils/avatarUtils.js +26 -0
  263. package/dist/esm/utils/cache.js +227 -0
  264. package/dist/esm/utils/cacheKey.js +80 -0
  265. package/dist/esm/utils/coldBoot.js +119 -0
  266. package/dist/esm/utils/commonsApproval.js +27 -0
  267. package/dist/esm/utils/commonsDelivery.js +54 -0
  268. package/dist/esm/utils/deviceManager.js +170 -0
  269. package/dist/esm/utils/displayNamePolicyRanges.generated.js +57 -0
  270. package/dist/esm/utils/edgeRegion.js +2 -0
  271. package/dist/esm/utils/errorUtils.js +232 -0
  272. package/dist/esm/utils/identityCacheSweep.js +92 -0
  273. package/dist/esm/utils/languageUtils.js +212 -0
  274. package/dist/esm/utils/oauthPkce.js +297 -0
  275. package/dist/esm/utils/officialOrigins.js +59 -0
  276. package/dist/esm/utils/oxyServiceEnvironment.js +16 -0
  277. package/dist/esm/utils/platform.js +110 -0
  278. package/dist/esm/utils/profileLinks.js +60 -0
  279. package/dist/esm/utils/redactUrl.js +26 -0
  280. package/dist/esm/utils/registrableApex.js +42 -0
  281. package/dist/esm/utils/requestUtils.js +203 -0
  282. package/dist/esm/utils/sessionUtils.js +171 -0
  283. package/dist/esm/utils/textNormalization.js +154 -0
  284. package/dist/esm/utils/userHandle.js +33 -0
  285. package/dist/esm/utils/userIdentity.js +45 -0
  286. package/dist/esm/utils/validationUtils.js +350 -0
  287. package/dist/esm/utils/webauthnOrigin.js +48 -0
  288. package/dist/types/.tsbuildinfo +1 -0
  289. package/dist/types/HttpService.d.ts +413 -0
  290. package/dist/types/OxyServices.base.d.ts +187 -0
  291. package/dist/types/OxyServices.d.ts +139 -0
  292. package/dist/types/OxyServices.errors.d.ts +84 -0
  293. package/dist/types/boot/sessionColdBoot.d.ts +140 -0
  294. package/dist/types/constants/version.d.ts +13 -0
  295. package/dist/types/crypto/aead.d.ts +56 -0
  296. package/dist/types/crypto/ecdh.d.ts +29 -0
  297. package/dist/types/crypto/identityMarker.d.ts +94 -0
  298. package/dist/types/crypto/kdf.d.ts +25 -0
  299. package/dist/types/crypto/keyManager.d.ts +582 -0
  300. package/dist/types/crypto/polyfill.d.ts +25 -0
  301. package/dist/types/crypto/recoveryPhrase.d.ts +173 -0
  302. package/dist/types/crypto/signatureService.d.ts +136 -0
  303. package/dist/types/i18n/accountCategoryLabels.d.ts +34 -0
  304. package/dist/types/i18n/accountRoleLabels.d.ts +10 -0
  305. package/dist/types/i18n/index.d.ts +3 -0
  306. package/dist/types/i18n/reputationCategoryLabels.d.ts +10 -0
  307. package/dist/types/i18n/trustTierLabels.d.ts +9 -0
  308. package/dist/types/index.d.ts +145 -0
  309. package/dist/types/inference/OxyInferenceClient.d.ts +342 -0
  310. package/dist/types/logger/index.d.ts +104 -0
  311. package/dist/types/mixins/OxyServices.accounts.d.ts +868 -0
  312. package/dist/types/mixins/OxyServices.analytics.d.ts +72 -0
  313. package/dist/types/mixins/OxyServices.appData.d.ts +108 -0
  314. package/dist/types/mixins/OxyServices.assets.d.ts +329 -0
  315. package/dist/types/mixins/OxyServices.auth.d.ts +908 -0
  316. package/dist/types/mixins/OxyServices.chains.d.ts +156 -0
  317. package/dist/types/mixins/OxyServices.civic.d.ts +510 -0
  318. package/dist/types/mixins/OxyServices.connectedApps.d.ts +169 -0
  319. package/dist/types/mixins/OxyServices.contacts.d.ts +100 -0
  320. package/dist/types/mixins/OxyServices.deviceBoot.d.ts +185 -0
  321. package/dist/types/mixins/OxyServices.deviceTransfer.d.ts +149 -0
  322. package/dist/types/mixins/OxyServices.devices.d.ts +103 -0
  323. package/dist/types/mixins/OxyServices.features.d.ts +250 -0
  324. package/dist/types/mixins/OxyServices.followGraph.d.ts +223 -0
  325. package/dist/types/mixins/OxyServices.identity.d.ts +351 -0
  326. package/dist/types/mixins/OxyServices.identityBackup.d.ts +129 -0
  327. package/dist/types/mixins/OxyServices.inference.d.ts +95 -0
  328. package/dist/types/mixins/OxyServices.language.d.ts +87 -0
  329. package/dist/types/mixins/OxyServices.links.d.ts +104 -0
  330. package/dist/types/mixins/OxyServices.location.d.ts +70 -0
  331. package/dist/types/mixins/OxyServices.nodes.d.ts +198 -0
  332. package/dist/types/mixins/OxyServices.notifications.d.ts +143 -0
  333. package/dist/types/mixins/OxyServices.payment.d.ts +117 -0
  334. package/dist/types/mixins/OxyServices.privacy.d.ts +146 -0
  335. package/dist/types/mixins/OxyServices.reputation.d.ts +202 -0
  336. package/dist/types/mixins/OxyServices.security.d.ts +84 -0
  337. package/dist/types/mixins/OxyServices.store.d.ts +334 -0
  338. package/dist/types/mixins/OxyServices.topics.d.ts +110 -0
  339. package/dist/types/mixins/OxyServices.user.d.ts +558 -0
  340. package/dist/types/mixins/OxyServices.utility.d.ts +393 -0
  341. package/dist/types/mixins/index.d.ts +87 -0
  342. package/dist/types/mixins/mixinHelpers.d.ts +31 -0
  343. package/dist/types/models/Topic.d.ts +42 -0
  344. package/dist/types/models/interfaces.d.ts +675 -0
  345. package/dist/types/models/session.d.ts +51 -0
  346. package/dist/types/server/auth.d.ts +130 -0
  347. package/dist/types/server/capabilityTicket.d.ts +41 -0
  348. package/dist/types/server/cors.d.ts +98 -0
  349. package/dist/types/server/index.d.ts +35 -0
  350. package/dist/types/server/rateLimit.d.ts +40 -0
  351. package/dist/types/server/safeFetch.d.ts +137 -0
  352. package/dist/types/server/securityHeaders.d.ts +221 -0
  353. package/dist/types/server/userInvalidation.d.ts +114 -0
  354. package/dist/types/server/verifySecret.d.ts +29 -0
  355. package/dist/types/session/SessionClient.d.ts +418 -0
  356. package/dist/types/session/accountDelegationAccess.d.ts +40 -0
  357. package/dist/types/session/accountDialogController.d.ts +648 -0
  358. package/dist/types/session/accountSwitchTargets.d.ts +70 -0
  359. package/dist/types/session/authStateStore.d.ts +153 -0
  360. package/dist/types/session/createSessionClient.d.ts +30 -0
  361. package/dist/types/session/deviceDirectory.d.ts +182 -0
  362. package/dist/types/session/deviceSwitcherRows.d.ts +92 -0
  363. package/dist/types/session/identityPin.d.ts +87 -0
  364. package/dist/types/session/identitySession.d.ts +105 -0
  365. package/dist/types/session/projectSessionState.d.ts +74 -0
  366. package/dist/types/session/refresh.d.ts +179 -0
  367. package/dist/types/session/sessionClientHost.d.ts +18 -0
  368. package/dist/types/session/sharedDeviceCredential.d.ts +202 -0
  369. package/dist/types/session/socketLoader.d.ts +11 -0
  370. package/dist/types/shared/utils/colorUtils.d.ts +104 -0
  371. package/dist/types/shared/utils/errorUtils.d.ts +97 -0
  372. package/dist/types/shared/utils/index.d.ts +13 -0
  373. package/dist/types/shared/utils/networkUtils.d.ts +139 -0
  374. package/dist/types/shared/utils/themeUtils.d.ts +90 -0
  375. package/dist/types/utils/accountCacheSweep.d.ts +75 -0
  376. package/dist/types/utils/accountUtils.d.ts +100 -0
  377. package/dist/types/utils/activityId.d.ts +2 -0
  378. package/dist/types/utils/apiUtils.d.ts +95 -0
  379. package/dist/types/utils/asyncUtils.d.ts +62 -0
  380. package/dist/types/utils/authHelpers.d.ts +58 -0
  381. package/dist/types/utils/authWebUrl.d.ts +16 -0
  382. package/dist/types/utils/avatarUtils.d.ts +16 -0
  383. package/dist/types/utils/cache.d.ts +130 -0
  384. package/dist/types/utils/cacheKey.d.ts +66 -0
  385. package/dist/types/utils/coldBoot.d.ts +131 -0
  386. package/dist/types/utils/commonsApproval.d.ts +13 -0
  387. package/dist/types/utils/commonsDelivery.d.ts +105 -0
  388. package/dist/types/utils/deviceManager.d.ts +65 -0
  389. package/dist/types/utils/displayNamePolicyRanges.generated.d.ts +57 -0
  390. package/dist/types/utils/edgeRegion.d.ts +2 -0
  391. package/dist/types/utils/errorUtils.d.ts +125 -0
  392. package/dist/types/utils/identityCacheSweep.d.ts +80 -0
  393. package/dist/types/utils/languageUtils.d.ts +100 -0
  394. package/dist/types/utils/oauthPkce.d.ts +157 -0
  395. package/dist/types/utils/officialOrigins.d.ts +13 -0
  396. package/dist/types/utils/oxyServiceEnvironment.d.ts +17 -0
  397. package/dist/types/utils/platform.d.ts +49 -0
  398. package/dist/types/utils/profileLinks.d.ts +44 -0
  399. package/dist/types/utils/redactUrl.d.ts +17 -0
  400. package/dist/types/utils/registrableApex.d.ts +27 -0
  401. package/dist/types/utils/requestUtils.d.ts +123 -0
  402. package/dist/types/utils/sessionUtils.d.ts +54 -0
  403. package/dist/types/utils/textNormalization.d.ts +103 -0
  404. package/dist/types/utils/userHandle.d.ts +26 -0
  405. package/dist/types/utils/userIdentity.d.ts +21 -0
  406. package/dist/types/utils/validationUtils.d.ts +268 -0
  407. package/dist/types/utils/webauthnOrigin.d.ts +32 -0
  408. package/package.json +168 -0
  409. package/src/HttpService.ts +1553 -0
  410. package/src/OxyServices.base.ts +478 -0
  411. package/src/OxyServices.errors.ts +122 -0
  412. package/src/OxyServices.ts +172 -0
  413. package/src/__tests__/authSocket.test.ts +96 -0
  414. package/src/__tests__/httpServiceActivityId.test.ts +49 -0
  415. package/src/__tests__/httpServiceAuthSelfAwait.test.ts +108 -0
  416. package/src/__tests__/httpServiceCache.test.ts +318 -0
  417. package/src/__tests__/httpServiceCsrf.test.ts +235 -0
  418. package/src/__tests__/httpServiceFormEncoded.test.ts +142 -0
  419. package/src/__tests__/httpServiceQueueDeadlock.test.ts +128 -0
  420. package/src/__tests__/httpServiceUnwrapEnvelope.test.ts +115 -0
  421. package/src/__tests__/inSessionRefresh.test.ts +260 -0
  422. package/src/__tests__/linkedClient.test.ts +370 -0
  423. package/src/__tests__/parseHttpErrorBody.test.ts +116 -0
  424. package/src/__tests__/serverValueImportsDeclared.test.ts +120 -0
  425. package/src/__tests__/userIdentity.test.ts +143 -0
  426. package/src/boot/__tests__/sessionColdBoot.identity.test.ts +393 -0
  427. package/src/boot/__tests__/sessionColdBoot.sharedDevice.test.ts +325 -0
  428. package/src/boot/__tests__/sessionColdBoot.test.ts +570 -0
  429. package/src/boot/sessionColdBoot.ts +529 -0
  430. package/src/constants/version.ts +15 -0
  431. package/src/crypto/__tests__/backupMaterial.test.ts +86 -0
  432. package/src/crypto/__tests__/cryptoPrimitives.test.ts +226 -0
  433. package/src/crypto/__tests__/identityMocks.ts +125 -0
  434. package/src/crypto/__tests__/keyManager.atomicity.test.ts +272 -0
  435. package/src/crypto/__tests__/keyManager.cacheSafety.test.ts +175 -0
  436. package/src/crypto/__tests__/keyManager.identityStatus.test.ts +217 -0
  437. package/src/crypto/__tests__/keyManager.recoveryLadder.test.ts +179 -0
  438. package/src/crypto/__tests__/keyManager.recoveryMnemonic.test.ts +138 -0
  439. package/src/crypto/__tests__/keyManager.storageMigration.test.ts +227 -0
  440. package/src/crypto/__tests__/keyManager.test.ts +329 -0
  441. package/src/crypto/__tests__/polyfill.test.ts +109 -0
  442. package/src/crypto/__tests__/recoveryPhrase.test.ts +61 -0
  443. package/src/crypto/__tests__/scopedSeed.test.ts +126 -0
  444. package/src/crypto/__tests__/signChallengeShared.test.ts +63 -0
  445. package/src/crypto/__tests__/signedRecord.test.ts +230 -0
  446. package/src/crypto/aead.ts +97 -0
  447. package/src/crypto/ecdh.ts +37 -0
  448. package/src/crypto/identityMarker.ts +291 -0
  449. package/src/crypto/kdf.ts +43 -0
  450. package/src/crypto/keyManager.ts +2607 -0
  451. package/src/crypto/polyfill.ts +143 -0
  452. package/src/crypto/recoveryPhrase.ts +337 -0
  453. package/src/crypto/signatureService.ts +350 -0
  454. package/src/i18n/__tests__/accountCategoryLabels.test.ts +62 -0
  455. package/src/i18n/__tests__/accountRoleLabels.test.ts +54 -0
  456. package/src/i18n/__tests__/reputationCategoryLabels.test.ts +56 -0
  457. package/src/i18n/__tests__/trustTierLabels.test.ts +47 -0
  458. package/src/i18n/accountCategoryLabels.ts +44 -0
  459. package/src/i18n/accountRoleLabels.ts +26 -0
  460. package/src/i18n/index.ts +88 -0
  461. package/src/i18n/locales/ar-SA.json +282 -0
  462. package/src/i18n/locales/ca-ES.json +282 -0
  463. package/src/i18n/locales/de-DE.json +282 -0
  464. package/src/i18n/locales/en-US.json +2161 -0
  465. package/src/i18n/locales/es-ES.json +2161 -0
  466. package/src/i18n/locales/fr-FR.json +282 -0
  467. package/src/i18n/locales/it-IT.json +282 -0
  468. package/src/i18n/locales/ja-JP.json +282 -0
  469. package/src/i18n/locales/ko-KR.json +282 -0
  470. package/src/i18n/locales/pt-PT.json +282 -0
  471. package/src/i18n/locales/zh-CN.json +282 -0
  472. package/src/i18n/reputationCategoryLabels.ts +20 -0
  473. package/src/i18n/trustTierLabels.ts +18 -0
  474. package/src/index.ts +881 -0
  475. package/src/inference/OxyInferenceClient.ts +888 -0
  476. package/src/inference/__tests__/OxyInferenceClient.test.ts +834 -0
  477. package/src/logger/__tests__/logger.test.ts +207 -0
  478. package/src/logger/index.ts +217 -0
  479. package/src/mixins/OxyServices.accounts.ts +1330 -0
  480. package/src/mixins/OxyServices.analytics.ts +53 -0
  481. package/src/mixins/OxyServices.appData.ts +158 -0
  482. package/src/mixins/OxyServices.assets.ts +861 -0
  483. package/src/mixins/OxyServices.auth.ts +1858 -0
  484. package/src/mixins/OxyServices.chains.ts +134 -0
  485. package/src/mixins/OxyServices.civic.ts +945 -0
  486. package/src/mixins/OxyServices.connectedApps.ts +174 -0
  487. package/src/mixins/OxyServices.contacts.ts +73 -0
  488. package/src/mixins/OxyServices.deviceBoot.ts +210 -0
  489. package/src/mixins/OxyServices.deviceTransfer.ts +393 -0
  490. package/src/mixins/OxyServices.devices.ts +120 -0
  491. package/src/mixins/OxyServices.features.ts +454 -0
  492. package/src/mixins/OxyServices.followGraph.ts +293 -0
  493. package/src/mixins/OxyServices.identity.ts +705 -0
  494. package/src/mixins/OxyServices.identityBackup.ts +250 -0
  495. package/src/mixins/OxyServices.inference.ts +57 -0
  496. package/src/mixins/OxyServices.language.ts +144 -0
  497. package/src/mixins/OxyServices.links.ts +107 -0
  498. package/src/mixins/OxyServices.location.ts +46 -0
  499. package/src/mixins/OxyServices.nodes.ts +337 -0
  500. package/src/mixins/OxyServices.notifications.ts +142 -0
  501. package/src/mixins/OxyServices.payment.ts +163 -0
  502. package/src/mixins/OxyServices.privacy.ts +219 -0
  503. package/src/mixins/OxyServices.reputation.ts +436 -0
  504. package/src/mixins/OxyServices.security.ts +110 -0
  505. package/src/mixins/OxyServices.store.ts +585 -0
  506. package/src/mixins/OxyServices.topics.ts +169 -0
  507. package/src/mixins/OxyServices.user.ts +1137 -0
  508. package/src/mixins/OxyServices.utility.ts +1370 -0
  509. package/src/mixins/__tests__/OxyServices.civic.test.ts +1095 -0
  510. package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +124 -0
  511. package/src/mixins/__tests__/OxyServices.deviceTransfer.test.ts +271 -0
  512. package/src/mixins/__tests__/OxyServices.identity.test.ts +376 -0
  513. package/src/mixins/__tests__/OxyServices.links.test.ts +154 -0
  514. package/src/mixins/__tests__/OxyServices.nodes.test.ts +341 -0
  515. package/src/mixins/__tests__/OxyServices.rotateKey.test.ts +276 -0
  516. package/src/mixins/__tests__/OxyServices.serviceAssetMetadata.test.ts +192 -0
  517. package/src/mixins/__tests__/OxyServices.serviceAssetMetadataBySha.test.ts +135 -0
  518. package/src/mixins/__tests__/accounts.test.ts +727 -0
  519. package/src/mixins/__tests__/appData.test.ts +203 -0
  520. package/src/mixins/__tests__/assetCredentials.test.ts +47 -0
  521. package/src/mixins/__tests__/assetUpload.test.ts +191 -0
  522. package/src/mixins/__tests__/backgroundCredential.httpIntegration.test.ts +135 -0
  523. package/src/mixins/__tests__/chains.test.ts +113 -0
  524. package/src/mixins/__tests__/commonsSignIn.test.ts +1144 -0
  525. package/src/mixins/__tests__/connectedApps.test.ts +123 -0
  526. package/src/mixins/__tests__/deviceTokenMintPinned.test.ts +113 -0
  527. package/src/mixins/__tests__/discoveryErrorHandling.test.ts +266 -0
  528. package/src/mixins/__tests__/followCacheInvalidation.test.ts +173 -0
  529. package/src/mixins/__tests__/followGraph.test.ts +147 -0
  530. package/src/mixins/__tests__/followGraphPagination.test.ts +271 -0
  531. package/src/mixins/__tests__/getFileDownloadUrl.test.ts +346 -0
  532. package/src/mixins/__tests__/getFollowStatuses.test.ts +95 -0
  533. package/src/mixins/__tests__/getUsersByIds.test.ts +149 -0
  534. package/src/mixins/__tests__/identityBackup.test.ts +315 -0
  535. package/src/mixins/__tests__/identityWriteCacheInvalidation.test.ts +407 -0
  536. package/src/mixins/__tests__/inferenceFactory.test.ts +58 -0
  537. package/src/mixins/__tests__/onTokensChanged.test.ts +129 -0
  538. package/src/mixins/__tests__/preSessionSkipAuth.test.ts +284 -0
  539. package/src/mixins/__tests__/privacyCacheInvalidation.test.ts +155 -0
  540. package/src/mixins/__tests__/reputation.test.ts +522 -0
  541. package/src/mixins/__tests__/serviceAuth.test.ts +832 -0
  542. package/src/mixins/__tests__/serviceTokenJwks.test.ts +208 -0
  543. package/src/mixins/__tests__/store.test.ts +304 -0
  544. package/src/mixins/__tests__/topics.test.ts +156 -0
  545. package/src/mixins/__tests__/userReadCacheBypass.test.ts +129 -0
  546. package/src/mixins/__tests__/userTokenAuth.test.ts +746 -0
  547. package/src/mixins/__tests__/verifyChallenge.test.ts +130 -0
  548. package/src/mixins/__tests__/verifyServiceActingAs.test.ts +167 -0
  549. package/src/mixins/__tests__/webauthnAuth.test.ts +205 -0
  550. package/src/mixins/index.ts +201 -0
  551. package/src/mixins/mixinHelpers.ts +69 -0
  552. package/src/models/Topic.ts +46 -0
  553. package/src/models/interfaces.ts +786 -0
  554. package/src/models/session.ts +55 -0
  555. package/src/server/__tests__/auth.test.ts +78 -0
  556. package/src/server/__tests__/capabilityTicket.test.ts +111 -0
  557. package/src/server/__tests__/cors.socket.test.ts +225 -0
  558. package/src/server/__tests__/cors.test.ts +158 -0
  559. package/src/server/__tests__/rateLimit.test.ts +257 -0
  560. package/src/server/__tests__/safeFetch.test.ts +232 -0
  561. package/src/server/__tests__/securityHeaders.test.ts +348 -0
  562. package/src/server/__tests__/serviceTokenAttribution.test.ts +396 -0
  563. package/src/server/__tests__/userInvalidation.test.ts +203 -0
  564. package/src/server/__tests__/verifySecret.test.ts +40 -0
  565. package/src/server/auth.ts +271 -0
  566. package/src/server/capabilityTicket.ts +258 -0
  567. package/src/server/cors.ts +277 -0
  568. package/src/server/index.ts +136 -0
  569. package/src/server/rateLimit.ts +313 -0
  570. package/src/server/safeFetch.ts +586 -0
  571. package/src/server/securityHeaders.ts +467 -0
  572. package/src/server/userInvalidation.ts +199 -0
  573. package/src/server/verifySecret.ts +52 -0
  574. package/src/session/SessionClient.ts +1001 -0
  575. package/src/session/__tests__/SessionClient.additive.test.ts +153 -0
  576. package/src/session/__tests__/SessionClient.broadcastChannel.test.ts +114 -0
  577. package/src/session/__tests__/SessionClient.diagnostics.test.ts +91 -0
  578. package/src/session/__tests__/SessionClient.directory.test.ts +688 -0
  579. package/src/session/__tests__/SessionClient.httpIntegration.test.ts +65 -0
  580. package/src/session/__tests__/SessionClient.identityPin.test.ts +175 -0
  581. package/src/session/__tests__/SessionClient.rest.test.ts +119 -0
  582. package/src/session/__tests__/SessionClient.serverEvents.test.ts +72 -0
  583. package/src/session/__tests__/SessionClient.socket.test.ts +202 -0
  584. package/src/session/__tests__/SessionClient.socketFactory.test.ts +81 -0
  585. package/src/session/__tests__/SessionClient.state.test.ts +78 -0
  586. package/src/session/__tests__/SessionClient.switchTokenOrder.test.ts +170 -0
  587. package/src/session/__tests__/accountDelegationAccess.test.ts +104 -0
  588. package/src/session/__tests__/accountDialogController.test.ts +1969 -0
  589. package/src/session/__tests__/accountDialogShape.test.ts +118 -0
  590. package/src/session/__tests__/accountSwitchTargets.test.ts +137 -0
  591. package/src/session/__tests__/authStateStore.test.ts +451 -0
  592. package/src/session/__tests__/deviceDirectory.test.ts +422 -0
  593. package/src/session/__tests__/deviceSwitcherRows.test.ts +223 -0
  594. package/src/session/__tests__/identityPin.test.ts +131 -0
  595. package/src/session/__tests__/identitySession.test.ts +194 -0
  596. package/src/session/__tests__/projectSessionState.test.ts +105 -0
  597. package/src/session/__tests__/refresh.identityPin.test.ts +307 -0
  598. package/src/session/__tests__/refresh.test.ts +443 -0
  599. package/src/session/__tests__/sessionIntegration.test.ts +209 -0
  600. package/src/session/__tests__/sharedDeviceCredential.test.ts +300 -0
  601. package/src/session/accountDelegationAccess.ts +72 -0
  602. package/src/session/accountDialogController.ts +1494 -0
  603. package/src/session/accountSwitchTargets.ts +93 -0
  604. package/src/session/authStateStore.ts +502 -0
  605. package/src/session/createSessionClient.ts +39 -0
  606. package/src/session/deviceDirectory.ts +269 -0
  607. package/src/session/deviceSwitcherRows.ts +145 -0
  608. package/src/session/identityPin.ts +310 -0
  609. package/src/session/identitySession.ts +206 -0
  610. package/src/session/projectSessionState.ts +137 -0
  611. package/src/session/refresh.ts +517 -0
  612. package/src/session/sessionClientHost.ts +39 -0
  613. package/src/session/sharedDeviceCredential.ts +349 -0
  614. package/src/session/socketLoader.ts +30 -0
  615. package/src/shared/utils/colorUtils.ts +155 -0
  616. package/src/shared/utils/errorUtils.ts +181 -0
  617. package/src/shared/utils/index.ts +59 -0
  618. package/src/shared/utils/networkUtils.ts +248 -0
  619. package/src/shared/utils/themeUtils.ts +115 -0
  620. package/src/types/buffer.d.ts +97 -0
  621. package/src/types/expo-crypto.d.ts +16 -0
  622. package/src/types/expo-secure-store.d.ts +17 -0
  623. package/src/utils/__tests__/accountUtils.test.ts +151 -0
  624. package/src/utils/__tests__/activityId.test.ts +45 -0
  625. package/src/utils/__tests__/asyncUtils.test.ts +187 -0
  626. package/src/utils/__tests__/authWebUrl.test.ts +15 -0
  627. package/src/utils/__tests__/cacheKey.test.ts +0 -0
  628. package/src/utils/__tests__/coldBoot.test.ts +496 -0
  629. package/src/utils/__tests__/commonsApproval.test.ts +60 -0
  630. package/src/utils/__tests__/identityCacheSweep.test.ts +151 -0
  631. package/src/utils/__tests__/languageUtils.test.ts +219 -0
  632. package/src/utils/__tests__/oauthPkce.test.ts +272 -0
  633. package/src/utils/__tests__/officialOrigins.test.ts +31 -0
  634. package/src/utils/__tests__/oxyServiceEnvironment.test.ts +7 -0
  635. package/src/utils/__tests__/profileLinks.test.ts +180 -0
  636. package/src/utils/__tests__/redactUrl.test.ts +33 -0
  637. package/src/utils/__tests__/registrableApex.test.ts +62 -0
  638. package/src/utils/__tests__/textNormalization.test.ts +205 -0
  639. package/src/utils/__tests__/validationUtils.test.ts +703 -0
  640. package/src/utils/__tests__/webauthnOrigin.test.ts +83 -0
  641. package/src/utils/accountCacheSweep.ts +93 -0
  642. package/src/utils/accountUtils.ts +194 -0
  643. package/src/utils/activityId.ts +2 -0
  644. package/src/utils/apiUtils.ts +137 -0
  645. package/src/utils/asyncUtils.ts +280 -0
  646. package/src/utils/authHelpers.ts +148 -0
  647. package/src/utils/authWebUrl.ts +17 -0
  648. package/src/utils/avatarUtils.ts +39 -0
  649. package/src/utils/cache.ts +273 -0
  650. package/src/utils/cacheKey.ts +93 -0
  651. package/src/utils/coldBoot.ts +218 -0
  652. package/src/utils/commonsApproval.ts +39 -0
  653. package/src/utils/commonsDelivery.ts +132 -0
  654. package/src/utils/deviceManager.ts +203 -0
  655. package/src/utils/displayNamePolicyRanges.generated.ts +69 -0
  656. package/src/utils/edgeRegion.ts +2 -0
  657. package/src/utils/errorUtils.ts +352 -0
  658. package/src/utils/identityCacheSweep.ts +104 -0
  659. package/src/utils/languageUtils.ts +256 -0
  660. package/src/utils/oauthPkce.ts +392 -0
  661. package/src/utils/officialOrigins.ts +62 -0
  662. package/src/utils/oxyServiceEnvironment.ts +17 -0
  663. package/src/utils/platform.ts +139 -0
  664. package/src/utils/profileLinks.ts +91 -0
  665. package/src/utils/redactUrl.ts +28 -0
  666. package/src/utils/registrableApex.ts +41 -0
  667. package/src/utils/requestUtils.ts +237 -0
  668. package/src/utils/sessionUtils.ts +206 -0
  669. package/src/utils/textNormalization.ts +162 -0
  670. package/src/utils/userHandle.ts +49 -0
  671. package/src/utils/userIdentity.ts +60 -0
  672. package/src/utils/validationUtils.ts +390 -0
  673. package/src/utils/webauthnOrigin.ts +52 -0
@@ -0,0 +1,529 @@
1
+ /**
2
+ * Device-first session cold boot for every consumer.
3
+ *
4
+ * On a fresh page load / app launch this resolves the device's session in a
5
+ * deterministic order, built on the pure `runColdBoot` primitive. It NEVER
6
+ * redirects to a login page: an unresolved boot ends in a signed-out state that
7
+ * the app renders with a "Sign in with Oxy" button.
8
+ *
9
+ * Ordered steps (first to yield a session wins):
10
+ * 1. `warm-token-plant` (web + native) — the fastest path: when the persisted
11
+ * store still holds a warm access token that is valid for more than the
12
+ * refresh lead window, plant it and yield the session with NO network
13
+ * round-trip. The background scheduler rotates it shortly after.
14
+ * 2. `device-secret-mint` (web + native) — the zero-cookie transport: when the
15
+ * origin persisted a `deviceId` + `deviceSecret`, mint a short access token
16
+ * with a single bearer-less POST to `/session/device/token` (no cookie, no
17
+ * navigation) and rotate the secret in-use.
18
+ * 3. `shared-device-adopt` (native, ACCOUNT mode) — this app has no credential
19
+ * of its own but a sibling official app already put one in the shared native
20
+ * slot: adopt it and mint. This is how a newly installed official app joins
21
+ * the device's existing session WITHOUT another QR and without ever touching
22
+ * the Commons private key.
23
+ * 4. `shared-key-signin` (native, ACCOUNT mode) — the legacy lane: re-mint by
24
+ * signing with the shared-keychain IDENTITY key. Retained as a recovery /
25
+ * compatibility path for devices whose apps have not yet published a shared
26
+ * device credential — OR `identity-key-signin` (IDENTITY mode) — re-mint
27
+ * from THIS device's primary identity key.
28
+ * 5. Signed out.
29
+ *
30
+ * Two session modes (see {@link RunSessionColdBootOptions.sessionMode}):
31
+ * - `account` (default) — the device's ACTIVE account owns the session. Every
32
+ * Oxy app but the identity vault boots this way; behaviour is unchanged.
33
+ * - `identity` — the owner of the local PRIMARY identity key owns the session,
34
+ * permanently, regardless of which account the device is switched to. Each
35
+ * step above is bound to the persisted identity pin, and the shared-keychain
36
+ * lane is replaced by the primary-key one (the shared slot is a CROSS-APP
37
+ * slot that may hold a different identity).
38
+ *
39
+ * ESM-safe (no `require()`); no react/react-native/expo imports.
40
+ */
41
+ import { runColdBoot, type ColdBootOutcome, type ColdBootStep } from '../utils/coldBoot';
42
+ import { isNative as detectNative } from '../utils/platform';
43
+ import { logger } from '../logger';
44
+ import { computeIdentityTag } from '../utils/cacheKey';
45
+ import { TOKEN_REFRESH_LEAD_MS, refreshDeviceSecretArm } from '../session/refresh';
46
+ import {
47
+ establishIdentitySession,
48
+ resolveIdentityPin,
49
+ type IdentityBinding,
50
+ } from '../session/identitySession';
51
+ import type { IdentityPin } from '../session/identityPin';
52
+ import {
53
+ decideSharedDeviceJoin,
54
+ type SharedDeviceCredentialStore,
55
+ } from '../session/sharedDeviceCredential';
56
+ import type { OxyServices } from '../OxyServices';
57
+ import type { AuthStateStore, PersistedAuthState } from '../session/authStateStore';
58
+ import type { DeviceSessionState } from '@oxy.so/contracts';
59
+
60
+ /**
61
+ * Who owns the session this boot resolves.
62
+ *
63
+ * - `account` — the device's active account (every ordinary Oxy app).
64
+ * - `identity` — the owner of the device's primary identity key (the identity
65
+ * vault). Requires {@link RunSessionColdBootOptions.identity}.
66
+ */
67
+ export type SessionMode = 'account' | 'identity';
68
+
69
+ /** The winning session shape a cold-boot step reports. */
70
+ export interface DeviceBootSession {
71
+ sessionId: string;
72
+ userId: string;
73
+ accessToken: string;
74
+ /** Server state already returned by the mint; absent on warm-token restore. */
75
+ state?: DeviceSessionState;
76
+ }
77
+
78
+ /** Why a cold boot ended without a session. */
79
+ export type SignedOutReason = 'no_session' | 'error';
80
+
81
+ export interface RunSessionColdBootOptions {
82
+ oxy: OxyServices;
83
+ store: AuthStateStore;
84
+ /** Platform hints; default derived from `@oxy.so/core`'s platform detection. */
85
+ platform?: { isWeb?: boolean; isNative?: boolean };
86
+ /** Invoked with the winning session (token already planted). */
87
+ onSession?: (session: DeviceBootSession & { via: string }) => void | Promise<void>;
88
+ /** Invoked when the boot ended signed out. */
89
+ onSignedOut?: (reason: SignedOutReason) => void | Promise<void>;
90
+ onStepError?: (id: string, error: unknown) => void;
91
+ /**
92
+ * HARD overall deadline (ms) for the whole ordered step chain, forwarded to
93
+ * {@link runColdBoot}. Defense-in-depth so a single non-settling network step
94
+ * (a black-hole network that neither connects nor rejects) can NEVER hang the
95
+ * boot — and therefore app routing — indefinitely. Inert on healthy loads
96
+ * (every step settles well under it); only trips on pathological networks.
97
+ * When omitted there is no overall deadline (unchanged behavior).
98
+ */
99
+ overallDeadlineMs?: number;
100
+ /**
101
+ * Invoked once per step abandoned because {@link overallDeadlineMs} expired
102
+ * before it settled. Forwarded to {@link runColdBoot}. Must not throw.
103
+ */
104
+ onStepDeadline?: (stepId: string) => void;
105
+ /**
106
+ * Best-effort connectivity hint. When it returns `true` the two NETWORK steps
107
+ * (`device-secret-mint`, `shared-key-signin`) are skipped — an offline device
108
+ * cannot mint, and attempting to would burn the whole deadline on a doomed
109
+ * request before routing settles. The pure-local `warm-token-plant` step is
110
+ * NEVER gated by this: an offline returning user with an unexpired persisted
111
+ * token must still boot authenticated. Only an EXPLICIT offline verdict should
112
+ * be returned; the caller resolves unknown/timeout to `false` (assume online)
113
+ * so a flaky probe can never falsely skip a real sign-in.
114
+ */
115
+ isOffline?: () => boolean;
116
+ /**
117
+ * Who owns the resolved session. Defaults to `'account'` — the device's active
118
+ * account — which is the behaviour every ordinary Oxy app has today and which
119
+ * this option leaves byte-for-byte unchanged.
120
+ *
121
+ * `'identity'` binds the boot to the owner of this device's PRIMARY identity
122
+ * key and REQUIRES {@link identity}. When it is missing the boot refuses to
123
+ * run any lane (an identity-bound client silently falling back to the device's
124
+ * active account is precisely the bug this mode exists to prevent) and
125
+ * resolves signed out.
126
+ */
127
+ sessionMode?: SessionMode;
128
+ /**
129
+ * The identity binding (pin store + key/signature access) used by
130
+ * `sessionMode: 'identity'`. Ignored in `'account'` mode.
131
+ */
132
+ identity?: IdentityBinding;
133
+ /**
134
+ * The cross-app native slot holding this device's shared DeviceSession
135
+ * credential, enabling the `shared-device-adopt` lane. Supplied by
136
+ * `@oxy.so/services` on native; absent on web, where each origin is its own
137
+ * device by design.
138
+ *
139
+ * IGNORED in `sessionMode: 'identity'`. The shared slot belongs to whichever
140
+ * principal signed in on this device; an identity-bound client must resolve
141
+ * its session from the local key alone, and adopting a device credential is
142
+ * exactly the drift that mode exists to prevent.
143
+ */
144
+ sharedDeviceCredential?: SharedDeviceCredentialStore;
145
+ }
146
+
147
+ /**
148
+ * Run the device-first cold boot. Resolves to the `runColdBoot` outcome and, as
149
+ * a side effect, invokes `onSession` (winning session, token already planted) or
150
+ * `onSignedOut` (no session).
151
+ */
152
+ export async function runSessionColdBoot(
153
+ opts: RunSessionColdBootOptions,
154
+ ): Promise<ColdBootOutcome<DeviceBootSession>> {
155
+ const { oxy, store } = opts;
156
+ const isNative = opts.platform?.isNative ?? detectNative();
157
+
158
+ // Non-null ONLY in identity mode; every `!== null` test below therefore reads
159
+ // as "is this boot identity-bound?" while also narrowing the binding.
160
+ const identityBinding = opts.sessionMode === 'identity' ? opts.identity ?? null : null;
161
+ if (opts.sessionMode === 'identity' && identityBinding === null) {
162
+ logger.error(
163
+ 'runSessionColdBoot: sessionMode "identity" requires an `identity` binding — refusing to run any lane (an identity-bound client must never adopt the device active account)',
164
+ undefined,
165
+ { component: 'sessionColdBoot', method: 'runSessionColdBoot' },
166
+ );
167
+ await opts.onSignedOut?.('error');
168
+ return { kind: 'unauthenticated' };
169
+ }
170
+
171
+ // Best-effort connectivity gate for the NETWORK steps only. A missing hint or
172
+ // any non-`true` verdict means "assume online" — never falsely skip a real
173
+ // sign-in on an ambiguous probe.
174
+ const isOffline = (): boolean => opts.isOffline?.() ?? false;
175
+
176
+ // Boot-local (not module-level) so it cannot leak across boots or break under
177
+ // bundler re-evaluation.
178
+ let signedOutReason: SignedOutReason = 'no_session';
179
+
180
+ // Boot-local memo for the identity pin. Resolved lazily INSIDE a step so the
181
+ // (local, but storage-backed) read is covered by `overallDeadlineMs`, and
182
+ // resolved at most once per boot so two steps cannot disagree. Always `null`
183
+ // in account mode.
184
+ let pinResolved = false;
185
+ let cachedPin: IdentityPin | null = null;
186
+ const getIdentityPin = async (): Promise<IdentityPin | null> => {
187
+ if (identityBinding === null) {
188
+ return null;
189
+ }
190
+ if (!pinResolved) {
191
+ cachedPin = await resolveIdentityPin(identityBinding);
192
+ pinResolved = true;
193
+ }
194
+ return cachedPin;
195
+ };
196
+
197
+ const steps: Array<ColdBootStep<DeviceBootSession>> = [];
198
+
199
+ // 1. warm-token-plant (web + native) — the fastest path. When the persisted
200
+ // store already holds a still-valid warm access token (its expiry more than
201
+ // the refresh lead window away) plus its owning session identity, plant it
202
+ // and yield the session IMMEDIATELY, skipping the blocking mint round-trip on
203
+ // first paint. The token is used AS-IS: this step NEVER mints, rotates, or
204
+ // persists anything. The proactive `startTokenRefreshScheduler` + the
205
+ // request-time preflight (both wired in the services provider) rotate it in
206
+ // the background; a revoked token self-heals via the 401 -> re-mint -> clear
207
+ // path. This exposure is sanctioned by `authStateStore.ts` (~L30-36): the
208
+ // warm token is short-lived and adds nothing over the already-persisted
209
+ // `deviceSecret`.
210
+ steps.push({
211
+ id: 'warm-token-plant',
212
+ run: async () => {
213
+ const persisted = await store.load();
214
+ if (!persisted?.accessToken || !persisted.sessionId || !persisted.userId || !persisted.expiresAt) {
215
+ return { kind: 'skip' };
216
+ }
217
+ // Guard a malformed `expiresAt` (Date.parse -> NaN): treat as not-valid and
218
+ // fall through to the mint lane. A token still inside the refresh lead
219
+ // window (or already expired) is likewise skipped — let the mint lane get a
220
+ // fresh one rather than plant a token about to expire.
221
+ const expiresAtMs = new Date(persisted.expiresAt).getTime();
222
+ if (!Number.isFinite(expiresAtMs) || expiresAtMs <= Date.now() + TOKEN_REFRESH_LEAD_MS) {
223
+ return { kind: 'skip' };
224
+ }
225
+ if (identityBinding !== null) {
226
+ // Identity-bound: a persisted token is only acceptable when it belongs to
227
+ // the PINNED account. Both the token's own claim (`computeIdentityTag`,
228
+ // the same derivation `SessionClient.applyState` uses) and the owning
229
+ // session identity must match — a token planted for another account by an
230
+ // earlier account-mode write is exactly the durable drift this gate stops.
231
+ const pin = await getIdentityPin();
232
+ if (
233
+ pin === null ||
234
+ persisted.userId !== pin.accountId ||
235
+ computeIdentityTag(persisted.accessToken) !== pin.accountId
236
+ ) {
237
+ return { kind: 'skip' };
238
+ }
239
+ }
240
+ oxy.setTokens(persisted.accessToken);
241
+ return {
242
+ kind: 'session',
243
+ session: {
244
+ sessionId: persisted.sessionId,
245
+ userId: persisted.userId,
246
+ accessToken: persisted.accessToken,
247
+ },
248
+ };
249
+ },
250
+ });
251
+
252
+ // 2. device-secret-mint (web + native) — the zero-cookie fast path. When the
253
+ // origin persisted a deviceId + deviceSecret, mint a short access token with
254
+ // a single bearer-less POST (no cookie, no navigation). The mint itself runs
255
+ // through `refreshDeviceSecretArm`, which acquires the client's PROCESS-WIDE
256
+ // single-flight, persists `nextDeviceSecret` BEFORE planting the token, and
257
+ // returns a classified outcome — so concurrent mint lanes share one in-flight
258
+ // request and the durable store always converges on the server's credential.
259
+ steps.push({
260
+ id: 'device-secret-mint',
261
+ // Network step — skip entirely when the caller reports the device offline so
262
+ // a doomed mint cannot burn the overall deadline before routing settles.
263
+ enabled: () => !isOffline(),
264
+ run: async () => {
265
+ const pin = await getIdentityPin();
266
+ if (identityBinding !== null && pin === null) {
267
+ // Identity-bound with no verified pin (never written, or cleared because
268
+ // the local key changed/vanished). An UNPINNED mint would adopt whichever
269
+ // account the device is currently switched to — the exact drift this mode
270
+ // exists to prevent. Fall through to `identity-key-signin`, which
271
+ // re-derives the account from the local key and rewrites the pin.
272
+ return { kind: 'skip' };
273
+ }
274
+ const result = await refreshDeviceSecretArm({ oxy, store, pin });
275
+ switch (result.status) {
276
+ case 'ok':
277
+ // The arm persisted nextDeviceSecret and planted the token.
278
+ return {
279
+ kind: 'session',
280
+ session: {
281
+ sessionId: result.sessionId,
282
+ userId: result.userId,
283
+ accessToken: result.token,
284
+ state: result.state,
285
+ },
286
+ };
287
+ case 'invalid-secret': {
288
+ // Stale/diverged secret — drop it so the mint lane stops firing. On
289
+ // native the shared-key step below can still recover; on web this ends
290
+ // signed out. Setting it undefined drops the key on the store's JSON
291
+ // serialization, and the mint guard treats undefined as absent.
292
+ const persisted = await store.load();
293
+ if (persisted) {
294
+ await store.save({ ...persisted, deviceSecret: undefined });
295
+ }
296
+ return { kind: 'skip' };
297
+ }
298
+ case 'no-session':
299
+ // Device known, no live session — authoritative signed-out. Keep the
300
+ // secret (the device may sign in again).
301
+ signedOutReason = 'no_session';
302
+ return { kind: 'skip' };
303
+ case 'persist-failed':
304
+ // The mint rotated the secret but it could not be durably persisted —
305
+ // refuse to advertise a session that will not survive a reload. Keep
306
+ // the secret; a later boot/attempt re-mints once storage recovers.
307
+ logger.error(
308
+ 'device-secret mint rotated the secret but it could not be durably persisted — not planting',
309
+ undefined,
310
+ { component: 'sessionColdBoot', method: 'device-secret-mint' },
311
+ );
312
+ return { kind: 'skip' };
313
+ case 'account-not-on-device':
314
+ // Pinned mint rejected: the pinned account is no longer a live account
315
+ // of this device session. The secret is healthy — keep it untouched and
316
+ // let `identity-key-signin` re-establish from the local key.
317
+ logger.debug(
318
+ 'pinned device-secret mint rejected (account_not_on_device) — keeping secret, re-establishing from the identity key',
319
+ { component: 'sessionColdBoot', method: 'device-secret-mint' },
320
+ );
321
+ return { kind: 'skip' };
322
+ case 'transient':
323
+ // Network / 5xx: keep the secret; a later attempt can succeed.
324
+ logger.debug(
325
+ 'device-secret mint failed (transient) — keeping secret',
326
+ { component: 'sessionColdBoot', method: 'device-secret-mint' },
327
+ );
328
+ return { kind: 'skip' };
329
+ case 'no-secret':
330
+ return { kind: 'skip' };
331
+ }
332
+ },
333
+ });
334
+
335
+ if (identityBinding !== null) {
336
+ // 3-identity. identity-key-signin — re-mint from THIS device's PRIMARY
337
+ // identity key (`getPublicKey` → challenge → sign → verify). It REPLACES
338
+ // `shared-key-signin`, which reads the CROSS-APP shared keychain slot and
339
+ // may therefore hold a different identity than the device's primary — the
340
+ // one thing an identity-bound client must never adopt. The server resolves
341
+ // the account from the verified signer, so the winning session is
342
+ // identity-authoritative and the pin is (re)written from it.
343
+ //
344
+ // Online-gated like every network step; `{ retry: false }` keeps the two
345
+ // round-trips single attempts (the refresh scheduler / 401 lane own later
346
+ // retries) so this step cannot multiply boot latency. On web
347
+ // `KeyManager.getPublicKey()` resolves to `null`, so it skips.
348
+ steps.push({
349
+ id: 'identity-key-signin',
350
+ enabled: () => !isOffline(),
351
+ run: async () => {
352
+ const established = await establishIdentitySession({
353
+ oxy,
354
+ store,
355
+ binding: identityBinding,
356
+ requestOptions: { retry: false },
357
+ });
358
+ const accessToken = established?.session.accessToken;
359
+ if (!established || !accessToken) {
360
+ return { kind: 'skip' };
361
+ }
362
+ return {
363
+ kind: 'session',
364
+ session: {
365
+ sessionId: established.session.sessionId,
366
+ userId: established.session.user.id,
367
+ accessToken,
368
+ },
369
+ };
370
+ },
371
+ });
372
+ } else if (opts.sharedDeviceCredential) {
373
+ // 3. shared-device-adopt (native, ACCOUNT mode) — join the device's existing
374
+ // session through the shared native credential slot.
375
+ //
376
+ // This is the lane that separates identity from session transport. What it
377
+ // reads is an ordinary, individually revocable `deviceId` + `deviceSecret`
378
+ // put there by a sibling official app — never the Commons private key. It
379
+ // is what lets a freshly installed official app land signed in with no QR,
380
+ // and it is why an ordinary app never needs identity-key access at all.
381
+ //
382
+ // `decideSharedDeviceJoin` gates it: an app that already holds its own
383
+ // credential is never moved, and an UNREADABLE slot is never mistaken for
384
+ // an empty one. The lane therefore cannot sign anyone out, in either
385
+ // upgrade order.
386
+ const sharedSlot = opts.sharedDeviceCredential;
387
+ steps.push({
388
+ id: 'shared-device-adopt',
389
+ // The adoption itself is local, but it is only worth committing alongside
390
+ // a mint that proves the credential — so the whole lane is online-gated
391
+ // like every other network step.
392
+ enabled: () => isNative && !isOffline(),
393
+ run: async () => {
394
+ const before = await store.load();
395
+ const decision = decideSharedDeviceJoin(before, await sharedSlot.read());
396
+ if (decision.action === 'skip') {
397
+ logger.debug(
398
+ `shared device credential not adopted (${decision.reason})`,
399
+ { component: 'sessionColdBoot', method: 'shared-device-adopt' },
400
+ );
401
+ return { kind: 'skip' };
402
+ }
403
+
404
+ // Restore the store to exactly what it held before this lane touched it.
405
+ // A credential we adopted and could not prove must not be left behind for
406
+ // the next boot's mint lane to keep retrying.
407
+ const revert = async (): Promise<void> => {
408
+ if (before) {
409
+ await store.save(before);
410
+ } else {
411
+ await store.clear();
412
+ }
413
+ };
414
+
415
+ const adopted: PersistedAuthState = {
416
+ // The mint fills both in from the device's live state; carrying the
417
+ // previous session's ids into a different device session would be a
418
+ // lie for however long the mint takes.
419
+ sessionId: '',
420
+ userId: '',
421
+ deviceId: decision.credential.deviceId,
422
+ deviceSecret: decision.credential.deviceSecret,
423
+ };
424
+ if (!(await store.save(adopted))) {
425
+ logger.error(
426
+ 'adopted the shared device credential but it could not be durably persisted — reverting',
427
+ undefined,
428
+ { component: 'sessionColdBoot', method: 'shared-device-adopt' },
429
+ );
430
+ await revert();
431
+ return { kind: 'skip' };
432
+ }
433
+
434
+ const result = await refreshDeviceSecretArm({ oxy, store, pin: null });
435
+ if (result.status === 'ok') {
436
+ return {
437
+ kind: 'session',
438
+ session: {
439
+ sessionId: result.sessionId,
440
+ userId: result.userId,
441
+ accessToken: result.token,
442
+ },
443
+ };
444
+ }
445
+
446
+ if (result.status === 'invalid-secret') {
447
+ // The one place we hold POSITIVE proof that the exact bytes in the
448
+ // shared slot are dead — the server rejected them by name. Clearing it
449
+ // signs nobody out (a credential the server does not recognise cannot
450
+ // be minting for anyone) and it is what stops a dead credential from
451
+ // blocking every future install: a stale slot owned by a different
452
+ // `deviceId` is otherwise never overwritten, by design.
453
+ await sharedSlot.clear();
454
+ } else if (result.status === 'no-session') {
455
+ signedOutReason = 'no_session';
456
+ }
457
+ await revert();
458
+ return { kind: 'skip' };
459
+ },
460
+ });
461
+ }
462
+
463
+ if (identityBinding === null) {
464
+ // 4. shared-key-signin (native) — the RECOVERY / COMPATIBILITY lane: sign a
465
+ // challenge with the shared-keychain IDENTITY key to re-mint a session.
466
+ //
467
+ // It runs LAST on purpose. Using the self-custody key to obtain an ordinary
468
+ // session is the over-sharing #937 sets out to end, so it is now reachable
469
+ // only on a device where no sibling app has published a shared device
470
+ // credential yet — an install that predates this lane, or one where the
471
+ // shared slot is unreadable. Its own `store.save` below feeds the shared
472
+ // slot through the mirroring store, so the FIRST boot that takes this lane
473
+ // is also the last one that needs to: every later app joins by credential.
474
+ //
475
+ // Native AND online: it is a network step (challenge + verify round-trips),
476
+ // so it is gated by the same offline hint as the mint lane. `{ retry: false }`
477
+ // keeps the two round-trips as single attempts — the refresh scheduler /
478
+ // 401 lane own later retries — so this step cannot multiply boot latency.
479
+ steps.push({
480
+ id: 'shared-key-signin',
481
+ enabled: () => isNative && !isOffline(),
482
+ run: async () => {
483
+ const session = await oxy.signInWithSharedIdentity({ requestOptions: { retry: false } });
484
+ if (!session?.accessToken) {
485
+ return { kind: 'skip' };
486
+ }
487
+ // `verifyChallenge` mints a rotating deviceSecret; persist it so the next
488
+ // boot can use the faster device-secret-mint lane (sockets + tab-focus
489
+ // re-mint depend on the credential being in the store).
490
+ if (session.deviceId && session.deviceSecret) {
491
+ await store.save({
492
+ sessionId: session.sessionId,
493
+ userId: session.user.id,
494
+ deviceId: session.deviceId,
495
+ deviceSecret: session.deviceSecret,
496
+ accessToken: session.accessToken,
497
+ expiresAt: session.expiresAt,
498
+ });
499
+ }
500
+ return {
501
+ kind: 'session',
502
+ session: {
503
+ sessionId: session.sessionId,
504
+ userId: session.user.id,
505
+ accessToken: session.accessToken,
506
+ },
507
+ };
508
+ },
509
+ });
510
+ }
511
+
512
+ const outcome = await runColdBoot<DeviceBootSession>({
513
+ steps,
514
+ overallDeadlineMs: opts.overallDeadlineMs,
515
+ onStepDeadline: opts.onStepDeadline,
516
+ onStepError: (id, error) => {
517
+ signedOutReason = 'error';
518
+ opts.onStepError?.(id, error);
519
+ },
520
+ });
521
+
522
+ if (outcome.kind === 'session') {
523
+ await opts.onSession?.({ ...outcome.session, via: outcome.via });
524
+ return outcome;
525
+ }
526
+
527
+ await opts.onSignedOut?.(signedOutReason);
528
+ return outcome;
529
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Package version and metadata constants
3
+ * This file is auto-generated to avoid runtime dependency on package.json
4
+ */
5
+
6
+ export const packageInfo = {
7
+ name: "@oxy.so/services",
8
+ version: "5.2.1",
9
+ description: "Reusable OxyHQ module to handle authentication, user management, reputation system (Oxy Trust) and more 🚀",
10
+ main: "lib/commonjs/node/index.js",
11
+ module: "lib/module/node/index.js",
12
+ types: "lib/typescript/node/index.d.ts"
13
+ } as const;
14
+
15
+ export const { name, version, description } = packageInfo;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * RecoveryPhraseService.deriveBackupMaterial — encrypted off-device backup key
3
+ * schedule (b3 Feature 1).
4
+ *
5
+ * The two outputs are derived from the FULL 64-byte BIP-39 seed via HKDF-SHA256
6
+ * with domain-separated `info` labels. These tests PIN the derivation against a
7
+ * fixed phrase so an accidental algorithm/library/label swap is caught as a
8
+ * regression, and assert the domain separation is real (backup key ≠ lookup id ≠
9
+ * the raw private key, all from the same seed).
10
+ *
11
+ * The fixed phrase is the canonical BIP-39 all-zero-entropy vector
12
+ * ("abandon…about"), whose seed is the published `5eb00bbd…ce9e38e4` — so the
13
+ * pinned outputs below are reproducible from any independent HKDF-SHA256
14
+ * implementation over that seed.
15
+ */
16
+ import { RecoveryPhraseService } from '../recoveryPhrase';
17
+ import { KeyManager } from '../keyManager';
18
+
19
+ const FIXED_PHRASE =
20
+ 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
21
+
22
+ // Pinned outputs (independently reproducible — see file header).
23
+ const EXPECTED_BACKUP_KEY_HEX = 'c6bb29585610550e2667a9d05f35d081f67dd8ccee20a04488c39b25f47a9e74';
24
+ const EXPECTED_LOOKUP_ID = '8cad137ca961bfc62a2ef329869e8369777737c7c5353a8d94bb70d888c0ad0d';
25
+ // The raw private key = seed[0:32] (the FROZEN phrase→privateKey derivation).
26
+ const EXPECTED_PRIVATE_KEY = '5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc1';
27
+
28
+ const toHex = (bytes: Uint8Array): string => Buffer.from(bytes).toString('hex');
29
+
30
+ describe('RecoveryPhraseService.deriveBackupMaterial (KDF vectors)', () => {
31
+ it('derives the pinned backupKey + lookupId for the fixed phrase', async () => {
32
+ const { backupKey, lookupId } = await RecoveryPhraseService.deriveBackupMaterial(FIXED_PHRASE);
33
+
34
+ expect(backupKey).toBeInstanceOf(Uint8Array);
35
+ expect(backupKey).toHaveLength(32);
36
+ expect(toHex(backupKey)).toBe(EXPECTED_BACKUP_KEY_HEX);
37
+
38
+ expect(lookupId).toBe(EXPECTED_LOOKUP_ID);
39
+ expect(lookupId).toMatch(/^[0-9a-f]{64}$/);
40
+ });
41
+
42
+ it('is deterministic (same phrase → identical material)', async () => {
43
+ const a = await RecoveryPhraseService.deriveBackupMaterial(FIXED_PHRASE);
44
+ const b = await RecoveryPhraseService.deriveBackupMaterial(FIXED_PHRASE);
45
+ expect(toHex(a.backupKey)).toBe(toHex(b.backupKey));
46
+ expect(a.lookupId).toBe(b.lookupId);
47
+ });
48
+
49
+ it('normalizes case/whitespace like the other phrase helpers', async () => {
50
+ const messy = ` ${FIXED_PHRASE.toUpperCase()} `;
51
+ const { backupKey, lookupId } = await RecoveryPhraseService.deriveBackupMaterial(messy);
52
+ expect(toHex(backupKey)).toBe(EXPECTED_BACKUP_KEY_HEX);
53
+ expect(lookupId).toBe(EXPECTED_LOOKUP_ID);
54
+ });
55
+
56
+ it('enforces real domain separation (backupKey ≠ lookupId ≠ raw private key)', async () => {
57
+ const { backupKey, lookupId } = await RecoveryPhraseService.deriveBackupMaterial(FIXED_PHRASE);
58
+ const privateKey = await RecoveryPhraseService.derivePrivateKeyFromPhrase(FIXED_PHRASE);
59
+
60
+ // Sanity: the frozen phrase→privateKey path is unchanged.
61
+ expect(privateKey).toBe(EXPECTED_PRIVATE_KEY);
62
+
63
+ // The three derivations from the SAME seed are mutually distinct — a device
64
+ // compromise leaking only the 32-byte private key reveals nothing about the
65
+ // backup key or lookup id (both need the full 64-byte seed).
66
+ expect(toHex(backupKey)).not.toBe(lookupId);
67
+ expect(toHex(backupKey)).not.toBe(privateKey);
68
+ expect(lookupId).not.toBe(privateKey);
69
+ });
70
+
71
+ it('produces a valid signing key material that is NOT the backup key', async () => {
72
+ // Extra guard: the backupKey must never coincide with a usable identity key
73
+ // for this phrase.
74
+ const { backupKey } = await RecoveryPhraseService.deriveBackupMaterial(FIXED_PHRASE);
75
+ const privateKey = await RecoveryPhraseService.derivePrivateKeyFromPhrase(FIXED_PHRASE);
76
+ const publicKey = KeyManager.derivePublicKey(privateKey);
77
+ expect(publicKey).toMatch(/^04[0-9a-f]+$/);
78
+ expect(toHex(backupKey)).not.toBe(privateKey);
79
+ });
80
+
81
+ it('rejects an invalid recovery phrase', async () => {
82
+ await expect(RecoveryPhraseService.deriveBackupMaterial('not a real phrase')).rejects.toThrow(
83
+ /Invalid recovery phrase/,
84
+ );
85
+ });
86
+ });