@noy-db/hub 0.4.0-pre.4 → 0.4.0-pre.5

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 (378) hide show
  1. package/README.md +2 -2
  2. package/dist/aggregate/index.js +4 -4
  3. package/dist/api-22ES33N3.js +20 -0
  4. package/dist/as/index.js +11 -11
  5. package/dist/attestation/index.js +20 -20
  6. package/dist/{backup-JL2GOTYQ.js → backup-YCHAC575.js} +11 -11
  7. package/dist/blobs/index.js +12 -12
  8. package/dist/broker/index.js +12 -12
  9. package/dist/by/index.js +2 -2
  10. package/dist/cargo/index.js +29 -29
  11. package/dist/{chunk-2RMDVWGB.js → chunk-2CRZOVDI.js} +2 -2
  12. package/dist/{chunk-T7S4V52L.js → chunk-2PDBYF2O.js} +4 -4
  13. package/dist/{chunk-WJDYZP3U.js → chunk-3LJYHE7S.js} +36 -36
  14. package/dist/chunk-3LJYHE7S.js.map +1 -0
  15. package/dist/{chunk-CCT4WZI6.js → chunk-3OWYOECI.js} +5 -5
  16. package/dist/chunk-3OWYOECI.js.map +1 -0
  17. package/dist/{chunk-L4OA7G5T.js → chunk-522MBPE5.js} +2 -2
  18. package/dist/{chunk-ZJGZDOQO.js → chunk-5ATANZSW.js} +6 -6
  19. package/dist/{chunk-M25ZIEB2.js → chunk-5HSSTOOG.js} +2 -2
  20. package/dist/{chunk-LLVW4V7R.js → chunk-5WLDZZ6S.js} +3 -3
  21. package/dist/{chunk-7B4GPCX7.js → chunk-6RPWILRH.js} +5 -5
  22. package/dist/{chunk-IGM563N5.js → chunk-6SYOHUHM.js} +6 -6
  23. package/dist/chunk-6SYOHUHM.js.map +1 -0
  24. package/dist/{chunk-HGVFJL63.js → chunk-7BIJS6II.js} +15 -5
  25. package/dist/{chunk-HGVFJL63.js.map → chunk-7BIJS6II.js.map} +1 -1
  26. package/dist/{chunk-KR5EGWI4.js → chunk-7ICV4ASA.js} +5 -5
  27. package/dist/{chunk-J3VW4J4E.js → chunk-7LP7FFJ2.js} +2 -2
  28. package/dist/chunk-7LP7FFJ2.js.map +1 -0
  29. package/dist/{chunk-5P5AAXYA.js → chunk-7U6AB7DZ.js} +7 -7
  30. package/dist/chunk-7U6AB7DZ.js.map +1 -0
  31. package/dist/{chunk-KMONEHDJ.js → chunk-AFAPBZAA.js} +2 -2
  32. package/dist/{chunk-GBEXTC6Z.js → chunk-BAAUFCJK.js} +3 -3
  33. package/dist/chunk-BAAUFCJK.js.map +1 -0
  34. package/dist/{chunk-CGPO3ZYB.js → chunk-BY5YBSXA.js} +2 -2
  35. package/dist/{chunk-3O62PVDW.js → chunk-CJVEAVYH.js} +5 -5
  36. package/dist/chunk-CJVEAVYH.js.map +1 -0
  37. package/dist/{chunk-3XVOLB75.js → chunk-CKUYQO2P.js} +3 -3
  38. package/dist/{chunk-2JZ763FV.js → chunk-D4LAJ737.js} +2 -2
  39. package/dist/{chunk-OOTTXUNV.js → chunk-DYZ5CCVM.js} +2 -2
  40. package/dist/{chunk-W7TCXZWU.js → chunk-EEZCB74X.js} +393 -716
  41. package/dist/chunk-EEZCB74X.js.map +1 -0
  42. package/dist/{chunk-N4FZ6ITM.js → chunk-EGU4PWQW.js} +2 -2
  43. package/dist/{chunk-JKWNG3TM.js → chunk-FHHYCBBP.js} +2 -2
  44. package/dist/{chunk-QUIG6BZT.js → chunk-FIP7KI2P.js} +26 -26
  45. package/dist/chunk-FIP7KI2P.js.map +1 -0
  46. package/dist/{chunk-5KI3REO3.js → chunk-FTCGEUOY.js} +7 -7
  47. package/dist/{chunk-5BB3N4KU.js → chunk-G4MFQXYX.js} +2 -2
  48. package/dist/{chunk-PEY6ZTCF.js → chunk-GEEJPID6.js} +9 -9
  49. package/dist/{chunk-PEY6ZTCF.js.map → chunk-GEEJPID6.js.map} +1 -1
  50. package/dist/{chunk-KVHWEPT2.js → chunk-GHEMAVTN.js} +2 -2
  51. package/dist/{chunk-OVFCAS7U.js → chunk-GP54LU5N.js} +9 -9
  52. package/dist/{chunk-XW7NWO4A.js → chunk-GTNRMJYH.js} +2 -2
  53. package/dist/{chunk-JW3XCZA2.js → chunk-HAPY7WFP.js} +11 -11
  54. package/dist/{chunk-4X6DJZHY.js → chunk-HQKRZIX6.js} +4 -4
  55. package/dist/{chunk-OWE4BNTM.js → chunk-I2XEBEEF.js} +4 -4
  56. package/dist/{chunk-WEC3JGBA.js → chunk-IGLU4ME7.js} +3 -3
  57. package/dist/{chunk-BZK6JEX4.js → chunk-ILM6X6CP.js} +5 -5
  58. package/dist/{chunk-2KL6NDD4.js → chunk-J5TEU77Q.js} +4 -4
  59. package/dist/{chunk-UEO5CHOP.js → chunk-JAMDDLIM.js} +13 -13
  60. package/dist/chunk-JAMDDLIM.js.map +1 -0
  61. package/dist/{chunk-V7RGFJ3Z.js → chunk-JIHH26WQ.js} +6 -6
  62. package/dist/{chunk-XGVDLQJE.js → chunk-JN3SGXJE.js} +3 -3
  63. package/dist/{chunk-XULHFSHO.js → chunk-K4BWPNJO.js} +5 -5
  64. package/dist/chunk-K4BWPNJO.js.map +1 -0
  65. package/dist/{chunk-BZEPJJOK.js → chunk-K7BADAOS.js} +4 -4
  66. package/dist/{chunk-BLB7P7LS.js → chunk-KJTIAECI.js} +17 -17
  67. package/dist/chunk-KJTIAECI.js.map +1 -0
  68. package/dist/{chunk-U73LHVPF.js → chunk-L2CAWCQL.js} +4 -4
  69. package/dist/{chunk-MSO2C3KG.js → chunk-LC7DSMHR.js} +2 -2
  70. package/dist/{chunk-JA5B3SLH.js → chunk-M6QA2KK2.js} +2 -2
  71. package/dist/{chunk-IUIVDVXM.js → chunk-MUPZ6IZX.js} +2 -2
  72. package/dist/{chunk-QAFWRGVD.js → chunk-OGVE4IJW.js} +7 -7
  73. package/dist/{chunk-BCOKZC4B.js → chunk-OHV6T5JB.js} +19 -19
  74. package/dist/chunk-OHV6T5JB.js.map +1 -0
  75. package/dist/{chunk-25ZDB25Z.js → chunk-OMSKGLYA.js} +3 -3
  76. package/dist/chunk-OMSKGLYA.js.map +1 -0
  77. package/dist/{chunk-2U7Y7BNJ.js → chunk-ONB627HU.js} +2 -2
  78. package/dist/{chunk-GYB2QK5H.js → chunk-OWLZONGX.js} +2 -2
  79. package/dist/{chunk-YHPUDB7Q.js → chunk-OWOT7CFC.js} +2 -2
  80. package/dist/{chunk-VEX6G3YL.js → chunk-P4NUJVWL.js} +2 -2
  81. package/dist/{chunk-NM4LVUXH.js → chunk-PTT2NB46.js} +2 -2
  82. package/dist/{chunk-2ITLQZV5.js → chunk-PWWWMRT7.js} +2 -2
  83. package/dist/{chunk-GS3Q2KOZ.js → chunk-QBOQI6PG.js} +2 -2
  84. package/dist/{chunk-JIMEB4C6.js → chunk-QMMOMBH6.js} +4 -4
  85. package/dist/{chunk-QDNVHA2F.js → chunk-QMZ5A6FQ.js} +2 -2
  86. package/dist/{chunk-FLHOTGOO.js → chunk-QVGEVZ5F.js} +3 -3
  87. package/dist/{chunk-AFBJHD55.js → chunk-QZJEYZ4I.js} +5 -5
  88. package/dist/{chunk-V3YB7RGR.js → chunk-R2N37AA6.js} +3 -3
  89. package/dist/{chunk-JLHKBAFY.js → chunk-RWW3I3PO.js} +3 -3
  90. package/dist/{chunk-IGAIOGCN.js → chunk-S4YERMGJ.js} +4 -4
  91. package/dist/{chunk-OGFPMCSP.js → chunk-SBOHNJUD.js} +2 -2
  92. package/dist/{chunk-QXPOEAKR.js → chunk-SC4JLMZI.js} +2 -2
  93. package/dist/{chunk-EDC6MLXV.js → chunk-SEU6QHH4.js} +2 -2
  94. package/dist/{chunk-F4COUZLL.js → chunk-TMR64XPL.js} +2 -2
  95. package/dist/{chunk-DEQ2JTBR.js → chunk-TNATF3HB.js} +3 -3
  96. package/dist/{chunk-GZ2GKOF6.js → chunk-TNJVDG2B.js} +3 -3
  97. package/dist/{chunk-5OZCCLNC.js → chunk-TSNRZSRC.js} +2 -2
  98. package/dist/{chunk-QCUH2G77.js → chunk-UTEATWHO.js} +6 -6
  99. package/dist/{chunk-JA5O7UEZ.js → chunk-UVBHM7AK.js} +6 -6
  100. package/dist/chunk-UVBHM7AK.js.map +1 -0
  101. package/dist/{chunk-HKR25MGX.js → chunk-V5JUAW4M.js} +10 -10
  102. package/dist/chunk-V5JUAW4M.js.map +1 -0
  103. package/dist/{chunk-YGBIBOCR.js → chunk-VEGQYPCP.js} +7 -7
  104. package/dist/{chunk-25G7U6RD.js → chunk-WQN5SM7P.js} +2 -2
  105. package/dist/{chunk-NOT7XA7Q.js → chunk-X3MWEFPT.js} +5 -5
  106. package/dist/chunk-XUFAFHQM.js +78 -0
  107. package/dist/chunk-XUFAFHQM.js.map +1 -0
  108. package/dist/{chunk-MEKAMERX.js → chunk-YCAJDK4O.js} +29 -29
  109. package/dist/chunk-YCAJDK4O.js.map +1 -0
  110. package/dist/{chunk-BVZGNMVM.js → chunk-YFFEF6HR.js} +3 -3
  111. package/dist/{chunk-H4SMS4O2.js → chunk-YJ3TREAC.js} +2 -2
  112. package/dist/{chunk-5SZ24DL5.js → chunk-YQQPNXNO.js} +6 -6
  113. package/dist/{chunk-5BHNQNBI.js → chunk-YTW6ZIPD.js} +1 -1
  114. package/dist/chunk-YTW6ZIPD.js.map +1 -0
  115. package/dist/{chunk-EW4HFWGJ.js → chunk-YWZEDG7F.js} +6 -6
  116. package/dist/{chunk-2VUISNGP.js → chunk-YXIVUWDU.js} +5 -5
  117. package/dist/{chunk-YG2EG6CB.js → chunk-YXZNGHID.js} +4 -4
  118. package/dist/{chunk-QYJIOWKN.js → chunk-YZN6NRTR.js} +9 -9
  119. package/dist/{chunk-IZX3WZBF.js → chunk-YZNKFYDC.js} +4 -4
  120. package/dist/{chunk-E2R5NRZD.js → chunk-Z4D3YGAB.js} +2 -2
  121. package/dist/{chunk-STR7WBOB.js → chunk-ZUDAK436.js} +2 -2
  122. package/dist/{chunk-ITI3SJRB.js → chunk-ZX5FNWH7.js} +2 -2
  123. package/dist/classified/index.js +5 -5
  124. package/dist/{classified-marker-7S5IEP6Q.js → classified-marker-52P3D72W.js} +3 -3
  125. package/dist/collection-facade-XKCFHSLR.js +44 -0
  126. package/dist/{computed-WZNKRLCY.js → computed-33DRJDOC.js} +3 -3
  127. package/dist/consent/index.js +9 -9
  128. package/dist/{cover-B5MY43CK.js → cover-5BSNJREH.js} +4 -4
  129. package/dist/{dead-filter-KRRD57MR.js → dead-filter-JXVCKS6W.js} +2 -2
  130. package/dist/delegation-WWZPEDK2.js +25 -0
  131. package/dist/derivations/index.js +12 -12
  132. package/dist/derive-CWWFEQ4N.js +21 -0
  133. package/dist/dispatch-PKQQFDBT.js +193 -0
  134. package/dist/dispatch-PKQQFDBT.js.map +1 -0
  135. package/dist/dispatch-SMXUO7W5.js +72 -0
  136. package/dist/dispatch-SMXUO7W5.js.map +1 -0
  137. package/dist/{enclave-YFKA3GJT.js → enclave-I5H3GINP.js} +11 -11
  138. package/dist/executor-FCJEH3FU.js +9 -0
  139. package/dist/executor-OHET7TF7.js +9 -0
  140. package/dist/executor-SHYLIBOM.js +26 -0
  141. package/dist/export-accessible-FBL6TDSK.js +23 -0
  142. package/dist/extract-partition-ZYIRYMTI.js +38 -0
  143. package/dist/{fanout-sidecar-HJV3A7PM.js → fanout-sidecar-WWXVHHUH.js} +9 -9
  144. package/dist/find-LEBUXVL7.js +11 -0
  145. package/dist/forget/index.js +9 -9
  146. package/dist/guards/index.js +3 -3
  147. package/dist/history/index.js +12 -12
  148. package/dist/i18n/index.js +14 -14
  149. package/dist/index.d.ts +13 -9
  150. package/dist/index.js +119 -118
  151. package/dist/index.js.map +1 -1
  152. package/dist/indexing/index.js +2 -2
  153. package/dist/issue-F76PAGIR.js +19 -0
  154. package/dist/kernel/collection.d.ts +0 -1
  155. package/dist/kernel/enclave/crypto.d.ts +8 -8
  156. package/dist/kernel/enclave/index.d.ts +2 -2
  157. package/dist/kernel/errors.d.ts +20 -20
  158. package/dist/kernel/noydb.d.ts +32 -91
  159. package/dist/kernel/types.d.ts +82 -41
  160. package/dist/kernel/validation.d.ts +33 -33
  161. package/dist/kernel/vault.d.ts +12 -149
  162. package/dist/kernel/via/dispatch.d.ts +7 -0
  163. package/dist/{ledger-SW6QQOAV.js → ledger-M2BKCNVS.js} +11 -11
  164. package/dist/liberate-ZP7UB57K.js +24 -0
  165. package/dist/{link-set-GNGGCFI4.js → link-set-ZU4E5J5S.js} +11 -11
  166. package/dist/materialized-views/index.js +19 -19
  167. package/dist/noydb-BNDGEK5X.js +71 -0
  168. package/dist/on/index.js +9 -9
  169. package/dist/overlay-views/index.js +4 -4
  170. package/dist/periods/index.js +13 -13
  171. package/dist/pod/index.js +11 -11
  172. package/dist/{policy-EAXSJNOT.js → policy-IY2YEA3A.js} +5 -5
  173. package/dist/port/at/index.d.ts +4 -4
  174. package/dist/port/with/collection-options.d.ts +171 -0
  175. package/dist/port/with/team-strategy.d.ts +1 -1
  176. package/dist/portability/index.js +8 -8
  177. package/dist/{post-register-A7HCFBP5.js → post-register-XIAHB6KF.js} +6 -6
  178. package/dist/query/index.js +6 -6
  179. package/dist/register-HB4TQZSV.js +23 -0
  180. package/dist/registry-6BMBFEXT.js +19 -0
  181. package/dist/registry-7CZWF3EQ.js +9 -0
  182. package/dist/registry-JOFO7RAH.js +18 -0
  183. package/dist/request-withdrawal-UN3TRHWI.js +31 -0
  184. package/dist/{reveal-PB3YG544.js → reveal-VWXRV2XU.js} +6 -6
  185. package/dist/revoke-OLO2OVR7.js +24 -0
  186. package/dist/satellites/index.js +1 -1
  187. package/dist/sealed-record/index.js +12 -12
  188. package/dist/{seed-EKF4DIBB.js → seed-CDBFOTIK.js} +12 -12
  189. package/dist/seed-CDBFOTIK.js.map +1 -0
  190. package/dist/session/index.js +9 -9
  191. package/dist/shadow/index.js +2 -2
  192. package/dist/signer-MGGKPYAN.js +25 -0
  193. package/dist/snapshots/index.js +10 -10
  194. package/dist/{stale-4UNPHWOU.js → stale-NBROTA66.js} +10 -10
  195. package/dist/storage-3R37QZLA.js +23 -0
  196. package/dist/{store-coordination-provider-3AELCZ4Y.js → store-coordination-provider-P3IU5JHD.js} +3 -3
  197. package/dist/sync/index.js +10 -10
  198. package/dist/team/index.js +20 -20
  199. package/dist/tiers/index.js +10 -10
  200. package/dist/to/index.js +1 -1
  201. package/dist/tx/index.js +3 -3
  202. package/dist/util/index.js +1 -1
  203. package/dist/{verify-MXN66IIH.js → verify-5X37OXRY.js} +5 -5
  204. package/dist/{walk-H4YE52S2.js → walk-E3OVLBFR.js} +11 -11
  205. package/dist/with/index.js +11 -11
  206. package/dist/with-audit/portability/export-accessible.d.ts +4 -4
  207. package/dist/with-audit/portability/request-withdrawal.d.ts +3 -3
  208. package/dist/with-audit/portability/withdraw-accessible.d.ts +2 -2
  209. package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
  210. package/dist/with-cargo/adopt-partition.d.ts +7 -7
  211. package/dist/with-cargo/index.d.ts +1 -1
  212. package/dist/with-commit/history/time-machine.d.ts +2 -2
  213. package/dist/with-formula/derivations/dispatch.d.ts +98 -0
  214. package/dist/with-formula/materialized-views/dispatch.d.ts +75 -0
  215. package/dist/with-party/custody/liberate.d.ts +3 -3
  216. package/dist/with-party/session/dev-unlock.d.ts +2 -2
  217. package/dist/with-party/session/session.d.ts +2 -2
  218. package/dist/with-party/team/deed.d.ts +8 -8
  219. package/dist/with-party/team/index.d.ts +2 -2
  220. package/dist/with-party/team/keyring.d.ts +19 -19
  221. package/dist/with-party/team/{managed-passphrase.d.ts → managed-secret.d.ts} +27 -27
  222. package/dist/with-party/team/noydb-facade.d.ts +41 -41
  223. package/dist/with-party/team/peer-recover.d.ts +13 -13
  224. package/dist/with-party/team/recovery.d.ts +5 -5
  225. package/dist/with-party/team/rotate-recover.d.ts +30 -30
  226. package/dist/with-pod/bundle.d.ts +30 -30
  227. package/dist/with-pod/format.d.ts +2 -2
  228. package/dist/withdraw-accessible-Q2EMATCU.js +28 -0
  229. package/package.json +3 -3
  230. package/dist/api-UPAT2O3M.js +0 -20
  231. package/dist/chunk-25ZDB25Z.js.map +0 -1
  232. package/dist/chunk-3O62PVDW.js.map +0 -1
  233. package/dist/chunk-5BHNQNBI.js.map +0 -1
  234. package/dist/chunk-5P5AAXYA.js.map +0 -1
  235. package/dist/chunk-BCOKZC4B.js.map +0 -1
  236. package/dist/chunk-BLB7P7LS.js.map +0 -1
  237. package/dist/chunk-CCT4WZI6.js.map +0 -1
  238. package/dist/chunk-GBEXTC6Z.js.map +0 -1
  239. package/dist/chunk-HKR25MGX.js.map +0 -1
  240. package/dist/chunk-IGM563N5.js.map +0 -1
  241. package/dist/chunk-J3VW4J4E.js.map +0 -1
  242. package/dist/chunk-JA5O7UEZ.js.map +0 -1
  243. package/dist/chunk-MEKAMERX.js.map +0 -1
  244. package/dist/chunk-QUIG6BZT.js.map +0 -1
  245. package/dist/chunk-UEO5CHOP.js.map +0 -1
  246. package/dist/chunk-W7TCXZWU.js.map +0 -1
  247. package/dist/chunk-WJDYZP3U.js.map +0 -1
  248. package/dist/chunk-XULHFSHO.js.map +0 -1
  249. package/dist/collection-facade-CMZGQ6IS.js +0 -44
  250. package/dist/delegation-UT76AQIG.js +0 -25
  251. package/dist/derive-SGRCCKVP.js +0 -21
  252. package/dist/executor-TEZYRDN3.js +0 -9
  253. package/dist/executor-WQQRU6DO.js +0 -9
  254. package/dist/executor-YORMIBWV.js +0 -26
  255. package/dist/export-accessible-HXMKW5LJ.js +0 -23
  256. package/dist/extract-partition-QGQ2R42J.js +0 -38
  257. package/dist/find-VV6PBIHK.js +0 -11
  258. package/dist/issue-7JJ4Z7E2.js +0 -19
  259. package/dist/liberate-FK7JI2QX.js +0 -24
  260. package/dist/noydb-N5IW6OFZ.js +0 -70
  261. package/dist/register-YBMRLZLO.js +0 -23
  262. package/dist/registry-3LXKNXWI.js +0 -9
  263. package/dist/registry-4V4YQCXJ.js +0 -19
  264. package/dist/registry-ZULMLCJK.js +0 -18
  265. package/dist/request-withdrawal-SEVUEUDG.js +0 -31
  266. package/dist/revoke-5QULL5GT.js +0 -24
  267. package/dist/seed-EKF4DIBB.js.map +0 -1
  268. package/dist/signer-ZMH35FT4.js +0 -25
  269. package/dist/storage-QOZKQMID.js +0 -23
  270. package/dist/withdraw-accessible-HPOVNG2G.js +0 -28
  271. /package/dist/{api-UPAT2O3M.js.map → api-22ES33N3.js.map} +0 -0
  272. /package/dist/{backup-JL2GOTYQ.js.map → backup-YCHAC575.js.map} +0 -0
  273. /package/dist/{chunk-2RMDVWGB.js.map → chunk-2CRZOVDI.js.map} +0 -0
  274. /package/dist/{chunk-T7S4V52L.js.map → chunk-2PDBYF2O.js.map} +0 -0
  275. /package/dist/{chunk-L4OA7G5T.js.map → chunk-522MBPE5.js.map} +0 -0
  276. /package/dist/{chunk-ZJGZDOQO.js.map → chunk-5ATANZSW.js.map} +0 -0
  277. /package/dist/{chunk-M25ZIEB2.js.map → chunk-5HSSTOOG.js.map} +0 -0
  278. /package/dist/{chunk-LLVW4V7R.js.map → chunk-5WLDZZ6S.js.map} +0 -0
  279. /package/dist/{chunk-7B4GPCX7.js.map → chunk-6RPWILRH.js.map} +0 -0
  280. /package/dist/{chunk-KR5EGWI4.js.map → chunk-7ICV4ASA.js.map} +0 -0
  281. /package/dist/{chunk-KMONEHDJ.js.map → chunk-AFAPBZAA.js.map} +0 -0
  282. /package/dist/{chunk-CGPO3ZYB.js.map → chunk-BY5YBSXA.js.map} +0 -0
  283. /package/dist/{chunk-3XVOLB75.js.map → chunk-CKUYQO2P.js.map} +0 -0
  284. /package/dist/{chunk-2JZ763FV.js.map → chunk-D4LAJ737.js.map} +0 -0
  285. /package/dist/{chunk-OOTTXUNV.js.map → chunk-DYZ5CCVM.js.map} +0 -0
  286. /package/dist/{chunk-N4FZ6ITM.js.map → chunk-EGU4PWQW.js.map} +0 -0
  287. /package/dist/{chunk-JKWNG3TM.js.map → chunk-FHHYCBBP.js.map} +0 -0
  288. /package/dist/{chunk-5KI3REO3.js.map → chunk-FTCGEUOY.js.map} +0 -0
  289. /package/dist/{chunk-5BB3N4KU.js.map → chunk-G4MFQXYX.js.map} +0 -0
  290. /package/dist/{chunk-KVHWEPT2.js.map → chunk-GHEMAVTN.js.map} +0 -0
  291. /package/dist/{chunk-OVFCAS7U.js.map → chunk-GP54LU5N.js.map} +0 -0
  292. /package/dist/{chunk-XW7NWO4A.js.map → chunk-GTNRMJYH.js.map} +0 -0
  293. /package/dist/{chunk-JW3XCZA2.js.map → chunk-HAPY7WFP.js.map} +0 -0
  294. /package/dist/{chunk-4X6DJZHY.js.map → chunk-HQKRZIX6.js.map} +0 -0
  295. /package/dist/{chunk-OWE4BNTM.js.map → chunk-I2XEBEEF.js.map} +0 -0
  296. /package/dist/{chunk-WEC3JGBA.js.map → chunk-IGLU4ME7.js.map} +0 -0
  297. /package/dist/{chunk-BZK6JEX4.js.map → chunk-ILM6X6CP.js.map} +0 -0
  298. /package/dist/{chunk-2KL6NDD4.js.map → chunk-J5TEU77Q.js.map} +0 -0
  299. /package/dist/{chunk-V7RGFJ3Z.js.map → chunk-JIHH26WQ.js.map} +0 -0
  300. /package/dist/{chunk-XGVDLQJE.js.map → chunk-JN3SGXJE.js.map} +0 -0
  301. /package/dist/{chunk-BZEPJJOK.js.map → chunk-K7BADAOS.js.map} +0 -0
  302. /package/dist/{chunk-U73LHVPF.js.map → chunk-L2CAWCQL.js.map} +0 -0
  303. /package/dist/{chunk-MSO2C3KG.js.map → chunk-LC7DSMHR.js.map} +0 -0
  304. /package/dist/{chunk-JA5B3SLH.js.map → chunk-M6QA2KK2.js.map} +0 -0
  305. /package/dist/{chunk-IUIVDVXM.js.map → chunk-MUPZ6IZX.js.map} +0 -0
  306. /package/dist/{chunk-QAFWRGVD.js.map → chunk-OGVE4IJW.js.map} +0 -0
  307. /package/dist/{chunk-2U7Y7BNJ.js.map → chunk-ONB627HU.js.map} +0 -0
  308. /package/dist/{chunk-GYB2QK5H.js.map → chunk-OWLZONGX.js.map} +0 -0
  309. /package/dist/{chunk-YHPUDB7Q.js.map → chunk-OWOT7CFC.js.map} +0 -0
  310. /package/dist/{chunk-VEX6G3YL.js.map → chunk-P4NUJVWL.js.map} +0 -0
  311. /package/dist/{chunk-NM4LVUXH.js.map → chunk-PTT2NB46.js.map} +0 -0
  312. /package/dist/{chunk-2ITLQZV5.js.map → chunk-PWWWMRT7.js.map} +0 -0
  313. /package/dist/{chunk-GS3Q2KOZ.js.map → chunk-QBOQI6PG.js.map} +0 -0
  314. /package/dist/{chunk-JIMEB4C6.js.map → chunk-QMMOMBH6.js.map} +0 -0
  315. /package/dist/{chunk-QDNVHA2F.js.map → chunk-QMZ5A6FQ.js.map} +0 -0
  316. /package/dist/{chunk-FLHOTGOO.js.map → chunk-QVGEVZ5F.js.map} +0 -0
  317. /package/dist/{chunk-AFBJHD55.js.map → chunk-QZJEYZ4I.js.map} +0 -0
  318. /package/dist/{chunk-V3YB7RGR.js.map → chunk-R2N37AA6.js.map} +0 -0
  319. /package/dist/{chunk-JLHKBAFY.js.map → chunk-RWW3I3PO.js.map} +0 -0
  320. /package/dist/{chunk-IGAIOGCN.js.map → chunk-S4YERMGJ.js.map} +0 -0
  321. /package/dist/{chunk-OGFPMCSP.js.map → chunk-SBOHNJUD.js.map} +0 -0
  322. /package/dist/{chunk-QXPOEAKR.js.map → chunk-SC4JLMZI.js.map} +0 -0
  323. /package/dist/{chunk-EDC6MLXV.js.map → chunk-SEU6QHH4.js.map} +0 -0
  324. /package/dist/{chunk-F4COUZLL.js.map → chunk-TMR64XPL.js.map} +0 -0
  325. /package/dist/{chunk-DEQ2JTBR.js.map → chunk-TNATF3HB.js.map} +0 -0
  326. /package/dist/{chunk-GZ2GKOF6.js.map → chunk-TNJVDG2B.js.map} +0 -0
  327. /package/dist/{chunk-5OZCCLNC.js.map → chunk-TSNRZSRC.js.map} +0 -0
  328. /package/dist/{chunk-QCUH2G77.js.map → chunk-UTEATWHO.js.map} +0 -0
  329. /package/dist/{chunk-YGBIBOCR.js.map → chunk-VEGQYPCP.js.map} +0 -0
  330. /package/dist/{chunk-25G7U6RD.js.map → chunk-WQN5SM7P.js.map} +0 -0
  331. /package/dist/{chunk-NOT7XA7Q.js.map → chunk-X3MWEFPT.js.map} +0 -0
  332. /package/dist/{chunk-BVZGNMVM.js.map → chunk-YFFEF6HR.js.map} +0 -0
  333. /package/dist/{chunk-H4SMS4O2.js.map → chunk-YJ3TREAC.js.map} +0 -0
  334. /package/dist/{chunk-5SZ24DL5.js.map → chunk-YQQPNXNO.js.map} +0 -0
  335. /package/dist/{chunk-EW4HFWGJ.js.map → chunk-YWZEDG7F.js.map} +0 -0
  336. /package/dist/{chunk-2VUISNGP.js.map → chunk-YXIVUWDU.js.map} +0 -0
  337. /package/dist/{chunk-YG2EG6CB.js.map → chunk-YXZNGHID.js.map} +0 -0
  338. /package/dist/{chunk-QYJIOWKN.js.map → chunk-YZN6NRTR.js.map} +0 -0
  339. /package/dist/{chunk-IZX3WZBF.js.map → chunk-YZNKFYDC.js.map} +0 -0
  340. /package/dist/{chunk-E2R5NRZD.js.map → chunk-Z4D3YGAB.js.map} +0 -0
  341. /package/dist/{chunk-STR7WBOB.js.map → chunk-ZUDAK436.js.map} +0 -0
  342. /package/dist/{chunk-ITI3SJRB.js.map → chunk-ZX5FNWH7.js.map} +0 -0
  343. /package/dist/{classified-marker-7S5IEP6Q.js.map → classified-marker-52P3D72W.js.map} +0 -0
  344. /package/dist/{collection-facade-CMZGQ6IS.js.map → collection-facade-XKCFHSLR.js.map} +0 -0
  345. /package/dist/{computed-WZNKRLCY.js.map → computed-33DRJDOC.js.map} +0 -0
  346. /package/dist/{cover-B5MY43CK.js.map → cover-5BSNJREH.js.map} +0 -0
  347. /package/dist/{dead-filter-KRRD57MR.js.map → dead-filter-JXVCKS6W.js.map} +0 -0
  348. /package/dist/{delegation-UT76AQIG.js.map → delegation-WWZPEDK2.js.map} +0 -0
  349. /package/dist/{derive-SGRCCKVP.js.map → derive-CWWFEQ4N.js.map} +0 -0
  350. /package/dist/{enclave-YFKA3GJT.js.map → enclave-I5H3GINP.js.map} +0 -0
  351. /package/dist/{executor-TEZYRDN3.js.map → executor-FCJEH3FU.js.map} +0 -0
  352. /package/dist/{executor-WQQRU6DO.js.map → executor-OHET7TF7.js.map} +0 -0
  353. /package/dist/{executor-YORMIBWV.js.map → executor-SHYLIBOM.js.map} +0 -0
  354. /package/dist/{export-accessible-HXMKW5LJ.js.map → export-accessible-FBL6TDSK.js.map} +0 -0
  355. /package/dist/{extract-partition-QGQ2R42J.js.map → extract-partition-ZYIRYMTI.js.map} +0 -0
  356. /package/dist/{fanout-sidecar-HJV3A7PM.js.map → fanout-sidecar-WWXVHHUH.js.map} +0 -0
  357. /package/dist/{find-VV6PBIHK.js.map → find-LEBUXVL7.js.map} +0 -0
  358. /package/dist/{issue-7JJ4Z7E2.js.map → issue-F76PAGIR.js.map} +0 -0
  359. /package/dist/{ledger-SW6QQOAV.js.map → ledger-M2BKCNVS.js.map} +0 -0
  360. /package/dist/{liberate-FK7JI2QX.js.map → liberate-ZP7UB57K.js.map} +0 -0
  361. /package/dist/{link-set-GNGGCFI4.js.map → link-set-ZU4E5J5S.js.map} +0 -0
  362. /package/dist/{noydb-N5IW6OFZ.js.map → noydb-BNDGEK5X.js.map} +0 -0
  363. /package/dist/{policy-EAXSJNOT.js.map → policy-IY2YEA3A.js.map} +0 -0
  364. /package/dist/{post-register-A7HCFBP5.js.map → post-register-XIAHB6KF.js.map} +0 -0
  365. /package/dist/{register-YBMRLZLO.js.map → register-HB4TQZSV.js.map} +0 -0
  366. /package/dist/{registry-3LXKNXWI.js.map → registry-6BMBFEXT.js.map} +0 -0
  367. /package/dist/{registry-4V4YQCXJ.js.map → registry-7CZWF3EQ.js.map} +0 -0
  368. /package/dist/{registry-ZULMLCJK.js.map → registry-JOFO7RAH.js.map} +0 -0
  369. /package/dist/{request-withdrawal-SEVUEUDG.js.map → request-withdrawal-UN3TRHWI.js.map} +0 -0
  370. /package/dist/{reveal-PB3YG544.js.map → reveal-VWXRV2XU.js.map} +0 -0
  371. /package/dist/{revoke-5QULL5GT.js.map → revoke-OLO2OVR7.js.map} +0 -0
  372. /package/dist/{signer-ZMH35FT4.js.map → signer-MGGKPYAN.js.map} +0 -0
  373. /package/dist/{stale-4UNPHWOU.js.map → stale-NBROTA66.js.map} +0 -0
  374. /package/dist/{storage-QOZKQMID.js.map → storage-3R37QZLA.js.map} +0 -0
  375. /package/dist/{store-coordination-provider-3AELCZ4Y.js.map → store-coordination-provider-P3IU5JHD.js.map} +0 -0
  376. /package/dist/{verify-MXN66IIH.js.map → verify-5X37OXRY.js.map} +0 -0
  377. /package/dist/{walk-H4YE52S2.js.map → walk-E3OVLBFR.js.map} +0 -0
  378. /package/dist/{withdraw-accessible-HPOVNG2G.js.map → withdraw-accessible-Q2EMATCU.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-cargo/active.ts","../src/with-cargo/describe-extraction.ts","../src/with-cargo/adopt-partition.ts","../src/with-cargo/decrypt-partition.ts"],"sourcesContent":["/**\n * Enable the cargo (partition extraction) capability.\n * Pass to `createNoydb({ cargoStrategy: withCargo() })` to make the source-side\n * `extractPartition(vault, ...)` free function live. The extraction crypto\n * engine is dynamically imported here, so it stays out of the floor bundle\n * until opted in. The recipient-side `adoptPartition` /\n * `decryptExtractedPartition` free functions — and `diffVault` (shared\n * import/merge infra) — are ungated and unaffected.\n */\nimport type { CargoStrategy } from './strategy.js'\n\nexport function withCargo(): CargoStrategy {\n return {\n async extractPartition(vault, opts) {\n const { extractPartitionCore } = await import('./extract-partition.js')\n return extractPartitionCore(vault, opts)\n },\n }\n}\n","/**\n * Partition-extraction dry-run. Read-only preview of what an\n * `extractPartition` would move: record counts, byte totals, and the\n * timestamp span per collection — computed from raw encrypted\n * envelopes WITHOUT decrypting them. Writes nothing, mutates nothing.\n *\n * @module\n */\nimport type { Vault } from '../kernel/vault.js'\nimport { walkClosure, type WalkClosureOptions, type DanglingRefNotice } from './walk-closure.js'\n\nexport interface ExtractionPreview {\n readonly totalRecords: number\n /** Sum of serialized encrypted-envelope sizes (bytes). */\n readonly totalBytes: number\n readonly byCollection: ReadonlyArray<{\n readonly name: string\n readonly recordCount: number\n readonly bytes: number\n /** Earliest envelope `_ts` in this collection (lexicographic). */\n readonly oldestTs?: string\n readonly newestTs?: string\n }>\n readonly graph: { readonly depth: number; readonly cyclesDetected: boolean }\n /** Records the walk reached but whose envelope couldn't be read. */\n readonly inaccessible: ReadonlyArray<{ readonly collection: string; readonly id: string }>\n /**\n * #772: outbound FK edges whose referenced parent was excluded from the\n * closure (missing, or tier-elevated and therefore invisible) — mirrors\n * `ExtractPartitionResult.danglingRefs` (#759) so a preview/extract pair\n * gives the SAME signal. A parent excluded this way never appears in\n * `byCollection` at all; this is the only preview-time indicator.\n */\n readonly danglingRefs: ReadonlyArray<DanglingRefNotice>\n}\n\nexport async function describeExtraction(\n vault: Vault,\n opts: WalkClosureOptions,\n): Promise<ExtractionPreview> {\n const { closure, graph, danglingRefs } = await walkClosure(vault, opts)\n\n const { name: vaultName, adapter } = vault._introspectState()\n const encoder = new TextEncoder()\n\n const byCollection: Array<{\n name: string; recordCount: number; bytes: number; oldestTs?: string; newestTs?: string\n }> = []\n const inaccessible: Array<{ collection: string; id: string }> = []\n let totalBytes = 0\n let totalRecords = 0\n\n for (const [collectionName, ids] of closure) {\n let bytes = 0\n let oldestTs: string | undefined\n let newestTs: string | undefined\n let recordCount = 0\n\n for (const id of ids) {\n const env = await adapter.get(vaultName, collectionName, id)\n if (!env) {\n // Walk reached it (via decrypted list) but the raw store read\n // returned nothing — surface rather than miscount.\n inaccessible.push({ collection: collectionName, id })\n continue\n }\n recordCount++\n bytes += encoder.encode(JSON.stringify(env)).length\n const ts = env._ts\n if (oldestTs === undefined || ts < oldestTs) oldestTs = ts\n if (newestTs === undefined || ts > newestTs) newestTs = ts\n }\n\n byCollection.push({\n name: collectionName,\n recordCount,\n bytes,\n // Spread conditionally — exactOptionalPropertyTypes forbids an\n // explicit `undefined` on an optional property.\n ...(oldestTs !== undefined ? { oldestTs } : {}),\n ...(newestTs !== undefined ? { newestTs } : {}),\n })\n totalBytes += bytes\n totalRecords += recordCount\n }\n\n byCollection.sort((a, b) => a.name.localeCompare(b.name))\n\n return Object.freeze({\n totalRecords,\n totalBytes,\n byCollection,\n graph,\n inaccessible,\n danglingRefs,\n })\n}\n","/**\n * Partition adoption. Recipient side: verify an extracted bundle,\n * validate the transfer key, import the re-keyed collections into a\n * destination store, and record an `_meta/adoption` marker. The bundle\n * stays UNOWNED after adoption — `createOwnerOnAdoptedPartition`\n * mints the owner; the transfer seal is then destroyed.\n *\n * @module\n */\nimport { base64ToBuffer, wrapKey, type EnclaveKey } from '../kernel/enclave/index.js'\nimport { TransferSealError, AdoptionStateError, ValidationError } from '../kernel/errors.js'\nimport type { NoydbStore, VaultSnapshot, KeyringFile } from '../kernel/types.js'\nimport { createOwnerKeyring } from '../with-party/team/keyring.js'\nimport { resolveManagedSecret } from '../with-party/team/managed-passphrase.js'\nimport type { SealingKeyProvider } from '../with-party/team/managed-passphrase.js'\nimport type { ShamirRecoveryProvider } from '../with-party/team/shamir-recovery-provider.js'\nimport type { RecoveryEnrollmentInput } from '../with-party/team/rotate-recover.js'\nimport { LedgerStore } from '../with-commit/history/ledger/store.js'\nimport { LEDGER_COLLECTION } from '../with-commit/history/ledger/constants.js'\nimport type { TransferSealPayload } from '../with-pod/bundle.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/bundle.js'\n\n/**\n * Reverse of `sealDeks`. Imports the transfer key, decrypts the\n * sealed `{ collection: base64(rawDEK) }` map (layout iv(12)‖ct‖tag), and\n * re-imports each DEK as an AES-GCM key. Throws `TransferSealError` on a\n * wrong key (AES-GCM auth-tag failure) or malformed payload.\n */\nexport async function unsealDeks(\n seal: TransferSealPayload,\n transferKey: Uint8Array,\n): Promise<Map<string, EnclaveKey>> {\n if (transferKey.byteLength !== 32) {\n throw new TransferSealError(\n `transfer key must be 32 bytes, got ${transferKey.byteLength}.`,\n )\n }\n const key = await crypto.subtle.importKey('raw', transferKey as BufferSource, 'AES-GCM', false, ['decrypt'])\n const raw = base64ToBuffer(seal.payload)\n let plaintext: ArrayBuffer\n try {\n plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: raw.slice(0, 12) as BufferSource },\n key,\n raw.slice(12) as BufferSource,\n )\n } catch {\n throw new TransferSealError(\n 'transfer seal could not be opened — wrong transfer key (AES-GCM authentication failed).',\n )\n }\n let dekMap: Record<string, string>\n try {\n dekMap = JSON.parse(new TextDecoder().decode(plaintext)) as Record<string, string>\n } catch {\n throw new TransferSealError('transfer seal payload is not valid JSON after decryption.')\n }\n const deks = new Map<string, EnclaveKey>()\n for (const [collection, b64] of Object.entries(dekMap)) {\n // Extractable: the recipient must be able to re-wrap these under their\n // own KEK (AES-KW) at owner-creation. Matches generateDEK.\n const dek = await crypto.subtle.importKey('raw', base64ToBuffer(b64) as BufferSource, 'AES-GCM', true, ['encrypt', 'decrypt'])\n deks.set(collection, dek)\n }\n return deks\n}\n\nexport interface AdoptPartitionOptions {\n readonly transferKey: Uint8Array\n readonly destinationStore: NoydbStore\n readonly vaultName: string\n}\n\nexport interface AdoptPartitionResult {\n readonly vaultName: string\n readonly needsOwner: true\n readonly sealId: string\n}\n\nexport async function adoptPartition(\n bundleBytes: Uint8Array,\n opts: AdoptPartitionOptions,\n): Promise<AdoptPartitionResult> {\n const { transferKey, destinationStore, vaultName } = opts\n\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new ValidationError(\n 'adoptPartition requires an extracted-partition bundle with a transfer seal. '\n + 'For ordinary backups use readPod + vault.load.',\n )\n }\n\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n\n // Validate the transfer key by unsealing in memory; throws\n // TransferSealError on mismatch. DEKs are discarded here — they stay\n // sealed at rest (in _meta/adoption) until owner-creation wraps them under the\n // recipient's KEK.\n await unsealDeks(seal, transferKey)\n\n // Single-occupancy per vaultName: an `_meta/adoption` marker already present\n // means this slot holds a partition (adopted-and-unowned, or already owned).\n // saveAll below would overwrite its data and replace the marker, stranding the\n // prior adoption's transfer seal. Refuse regardless of sealId — re-adopting the\n // SAME bundle is a redundant call, and adopting a DIFFERENT bundle here would\n // clobber the existing partition. Either way, pick a fresh vaultName.\n const existing = await destinationStore.get(vaultName, '_meta', 'adoption')\n if (existing) {\n const prior = JSON.parse(existing._data) as { sealId?: string }\n if (prior.sealId === seal.sealId) {\n throw new AdoptionStateError(\n `partition (sealId ${seal.sealId}) is already adopted into vault \"${vaultName}\".`,\n )\n }\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds an adopted partition (sealId ${prior.sealId}); `\n + `adopting a different partition (sealId ${seal.sealId}) here would overwrite it. `\n + `Adopt into a fresh vaultName instead.`,\n )\n }\n\n // The marker-only check above misses a worse case: a vaultName already in use\n // by an ORDINARY vault (createNoydb + openVault) carries no `_meta/adoption`,\n // yet `saveAll` below is destructive on SQL adapters (`DELETE FROM ... WHERE\n // vault = ?` followed by upsert) and would wipe the legitimate keyring +\n // data. Refuse adoption into ANY occupied slot — a fresh vaultName is the\n // documented precondition.\n const existingKeyring = await destinationStore.list(vaultName, '_keyring')\n if (existingKeyring.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds a keyring (an unrelated owner exists at this slot); `\n + `adoptPartition requires a fresh vaultName to avoid destructive saveAll on SQL adapters.`,\n )\n }\n\n const backup = JSON.parse(dump) as { collections: VaultSnapshot; _internal?: VaultSnapshot }\n await destinationStore.saveAll(vaultName, backup.collections)\n\n // Import carried internal collections (e.g. _schemas from carrySchemas).\n // saveAll only writes data collections; _internal is written per-record.\n if (backup._internal) {\n for (const [collection, records] of Object.entries(backup._internal)) {\n for (const [id, envelope] of Object.entries(records)) {\n await destinationStore.put(vaultName, collection, id, envelope)\n }\n }\n }\n\n const adoptedAt = new Date().toISOString()\n const adoption = { sealId: seal.sealId, adoptedAt, needsOwner: true as const, transferSeal: seal }\n await destinationStore.put(vaultName, '_meta', 'adoption', {\n _noydb: 1, _v: 1, _ts: adoptedAt, _iv: '', _data: JSON.stringify(adoption),\n })\n\n return { vaultName, needsOwner: true, sealId: seal.sealId }\n}\n\nexport interface CreateOwnerResult {\n readonly vaultName: string\n readonly userId: string\n}\n\n/** Standard-mode owner: recipient supplies the passphrase. */\nexport interface CreateOwnerStandardOptions {\n readonly userId: string\n readonly passphrase: string\n readonly transferKey: Uint8Array\n}\n\n/**\n * Managed-mode owner: the passphrase is minted + sealed under\n * a `SealingKeyProvider` (e.g. an `at-*` OS keychain) so the partition\n * auto-unlocks on the recipient's device. Managed mode mandates a strong\n * (Shamir) recovery profile at creation, which needs the\n * `shamirRecovery` provider injected.\n */\nexport interface CreateOwnerManagedOptions {\n readonly userId: string\n readonly passphraseMode: 'managed'\n readonly sealingKey: SealingKeyProvider\n readonly recovery: ReadonlyArray<RecoveryEnrollmentInput>\n readonly shamirRecovery: ShamirRecoveryProvider\n readonly transferKey: Uint8Array\n}\n\nexport type CreateOwnerOptions = CreateOwnerStandardOptions | CreateOwnerManagedOptions\n\nfunction isManaged(o: CreateOwnerOptions): o is CreateOwnerManagedOptions {\n return 'passphraseMode' in o && o.passphraseMode === 'managed'\n}\n\n/**\n * Mint the first owner keyring on an adopted-but-unowned partition,\n * then destroy the transfer seal.\n *\n * Standard mode: the recipient supplies a passphrase. Managed mode: the\n * passphrase is minted + sealed under a `SealingKeyProvider` and a strong\n * (Shamir) recovery profile is enrolled — orchestrated via the existing\n * `openVaultAndEnrollRecovery` ceremony.\n *\n * Either way, reuses `createOwnerKeyring` to derive the KEK + write the base\n * keyring, then wraps the partition's DEKs (recovered from the seal) under that\n * KEK and re-persists the merged keyring file.\n *\n * Idempotent under retry: the seal is destroyed LAST (Stage D), after the\n * keyring (Stage A), the ledger transition (Stage B), and — in managed mode —\n * strong-recovery enrollment (Stage C). A failure in the fallible enrollment\n * step leaves the seal intact, and re-running with the same `userId` +\n * `transferKey` resumes from the first incomplete stage. (Multi-profile recovery\n * arrays may re-enroll an already-enrolled profile on retry; managed mode's\n * mandated single Shamir profile does not.)\n */\nexport async function createOwnerOnAdoptedPartition(\n store: NoydbStore,\n vaultName: string,\n opts: CreateOwnerOptions,\n): Promise<CreateOwnerResult> {\n const { userId, transferKey } = opts\n\n // Managed mode requires a strong (Shamir) recovery profile, validated BEFORE\n // any disk write — same gate as createNoydb.\n if (isManaged(opts) && !opts.recovery.some((r) => r.profile === 'shamir')) {\n throw new AdoptionStateError(\n 'managed-mode adoption requires at least one strong (shamir) recovery profile in '\n + '`recovery` — paper alone is not strong when there is no user passphrase to fall back on.',\n )\n }\n\n // 1. Verify adopted-unowned state.\n const adoptionEnv = await store.get(vaultName, '_meta', 'adoption')\n if (!adoptionEnv) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" is not an adopted partition (no _meta/adoption). `\n + `createOwnerOnAdoptedPartition only applies to vaults created via adoptPartition.`,\n )\n }\n const adoption = JSON.parse(adoptionEnv._data) as {\n sealId: string; adoptedAt: string; needsOwner?: boolean\n consumedAt?: string; transferSeal?: TransferSealPayload\n }\n if (adoption.consumedAt !== undefined || adoption.transferSeal === undefined) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has an owner (transfer seal consumed at ${adoption.consumedAt}).`,\n )\n }\n\n // 2. Recover the partition DEKs from the seal (throws on wrong key) BEFORE\n // writing any keyring, so a bad transfer key leaves no trace. Always\n // validated, including when resuming a partial prior call.\n const partitionDeks = await unsealDeks(adoption.transferSeal, transferKey)\n\n // The ceremony below is split into stages so a failure in the fallible\n // managed-enrollment step (network/provider outage) leaves the call RETRYABLE\n // — the seal is destroyed only once everything durable is in place. Each stage\n // detects its own prior completion rather than relying on a single resume bit.\n\n // A keyring present for a DIFFERENT user (with the seal still unconsumed) is a\n // genuine second-owner attempt — refuse it. A same-user keyring is a resumed\n // partial call and is handled by the stage checks below.\n const existingKeyring = await store.get(vaultName, '_keyring', userId)\n const otherOwners = (await store.list(vaultName, '_keyring')).filter((u) => u !== userId)\n if (otherOwners.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has a keyring for a different owner; cannot create owner \"${userId}\".`,\n )\n }\n\n // Stage A — mint the owner keyring + merge the partition DEKs. Considered done\n // only when the keyring already holds every partition DEK. createOwnerKeyring\n // overwrites (fresh KEK + fresh _users DEK), so re-running is safe ONLY while\n // no recovery has been enrolled yet — guaranteed here because enrollment\n // (Stage C) runs strictly after Stage A completes.\n const partitionCollections = [...partitionDeks.keys()]\n const priorDeks = existingKeyring ? (JSON.parse(existingKeyring._data) as KeyringFile).deks : {}\n const ownerMinted = existingKeyring !== null && partitionCollections.every((c) => c in priorDeks)\n if (!ownerMinted) {\n // Resolve the owner passphrase. Managed mode mints a random passphrase, seals\n // it under the provider, and persists _meta/sealed-passphrase (so the\n // partition auto-unlocks on the recipient's device); standard mode uses the\n // caller's passphrase. Idempotent under retry — resolveManagedSecret's reopen\n // arm reuses an already-sealed passphrase.\n const passphrase = isManaged(opts)\n ? await resolveManagedSecret(store, vaultName, opts.sealingKey)\n : opts.passphrase\n\n // Mint the owner keyring (KEK + _users DEK + canary, written to disk).\n const unlocked = await createOwnerKeyring(store, vaultName, userId, passphrase)\n\n // Merge the partition DEKs (wrapped under the new KEK) into the keyring.\n const env = await store.get(vaultName, '_keyring', userId)\n if (!env) throw new AdoptionStateError(`keyring write for \"${userId}\" did not persist`)\n const keyringFile = JSON.parse(env._data) as KeyringFile\n const kek = unlocked.kek\n if (!kek) throw new AdoptionStateError(`owner keyring for \"${userId}\" has no KEK to wrap partition DEKs under`)\n const mergedDeks: Record<string, string> = { ...keyringFile.deks }\n for (const [collection, dek] of partitionDeks) {\n mergedDeks[collection] = await wrapKey(dek, kek)\n }\n const mergedFile: KeyringFile = { ...keyringFile, deks: mergedDeks }\n await store.put(vaultName, '_keyring', userId, { ...env, _data: JSON.stringify(mergedFile) })\n }\n\n // Stage B — record the ownership transition on the carried\n // audit chain (carryLedger sealed the _ledger DEK). No-op without that DEK.\n // Idempotent: appended only if the closing `transfer-seal-consumed` entry is\n // absent, so a retry does not duplicate the pair.\n const ledgerDek = partitionDeks.get(LEDGER_COLLECTION)\n if (ledgerDek) {\n const ledger = new LedgerStore({\n adapter: store,\n vault: vaultName,\n encrypted: true,\n getDEK: async () => ledgerDek,\n actor: userId,\n })\n const creationReason = `creation-of-new-owner:${userId}`\n const consumedReason = `transfer-seal-consumed:${adoption.sealId}`\n // Gate each append on its own presence — a crash or store error strictly\n // between the two adjacent puts would otherwise re-append the first one\n // on retry. The pair is the audit record, not a single transaction.\n const recordedReasons = new Set((await ledger.loadAllEntries()).map((e) => e.reason))\n if (!recordedReasons.has(creationReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: creationReason })\n }\n if (!recordedReasons.has(consumedReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: consumedReason })\n }\n }\n\n // Stage C — Managed mode: enroll the mandatory strong recovery\n // by orchestrating the existing public ceremony. The partition is\n // now a managed-mode vault on disk (sealed passphrase + keyring), so we\n // open it as a normal client and let openVaultAndEnrollRecovery do the\n // gate-bypass + enroll + re-assert. Dynamic import keeps the Noydb class\n // out of the @noy-db/hub/cargo static graph. Runs BEFORE seal destruction\n // so a failure here leaves the seal intact and the call retryable.\n if (isManaged(opts)) {\n const { createNoydb } = await import('../kernel/noydb.js')\n const db = await createNoydb({\n store,\n user: userId,\n passphraseMode: 'managed',\n sealingKey: opts.sealingKey,\n shamirRecovery: opts.shamirRecovery,\n })\n await db.openVaultAndEnrollRecovery(vaultName, { recovery: opts.recovery })\n }\n\n // Stage D — Destroy the transfer seal LAST — the commit point. Everything\n // above is either idempotent or resumable, so the seal is only consumed\n // once the owner keyring (and, in managed mode, strong recovery) is\n // durably in place. Retain sealId + consumedAt for audit.\n const consumed = { sealId: adoption.sealId, adoptedAt: adoption.adoptedAt, consumedAt: new Date().toISOString() }\n await store.put(vaultName, '_meta', 'adoption', { ...adoptionEnv, _data: JSON.stringify(consumed) })\n\n return { vaultName, userId }\n}\n","/**\n * Read-side counterpart to `extractPartition`: decrypt an\n * extracted-partition bundle's records to plaintext using its transfer\n * key, WITHOUT adopting it into a vault. Used by an outward\n * orchestration layer's reconcile/merge and field-authority flows to\n * compare incoming records against a receiver. The transfer key validates the bundle\n * (wrong key throws).\n * @module\n */\nimport type { EncryptedEnvelope } from '../kernel/types.js'\nimport { openEnvelopeJson } from '../kernel/enclave/index.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/bundle.js'\nimport { unsealDeks } from './adopt-partition.js'\n\n/** One decrypted record from an extracted-partition compartment. */\nexport interface DecryptedRecord {\n readonly id: string\n readonly record: Record<string, unknown>\n /** Source envelope write timestamp (ISO) — for last-write-wins merges. */\n readonly ts: string\n /** Source envelope version. */\n readonly version: number\n /** Provenance source id (FR-5). Present only when the source collection had provenance:true and a source was supplied on put. */\n readonly source?: string\n /** ISO-8601 timestamp the provenance source was recorded (FR-5). */\n readonly sourceTs?: string\n}\n\n/**\n * Decrypt every record of an extracted-partition bundle to plaintext,\n * grouped by collection. Throws if the bundle isn't an\n * extracted-partition or the transfer key is wrong.\n */\nexport async function decryptExtractedPartition(\n bundleBytes: Uint8Array,\n transferKey: Uint8Array,\n): Promise<Record<string, DecryptedRecord[]>> {\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new Error('decryptExtractedPartition: bundle is not an extracted-partition.')\n }\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n const deks = await unsealDeks(seal, transferKey) // throws TransferSealError on wrong key\n const backup = JSON.parse(dump) as { collections: Record<string, Record<string, EncryptedEnvelope>> }\n const out: Record<string, DecryptedRecord[]> = {}\n for (const [collection, byId] of Object.entries(backup.collections)) {\n const dek = deks.get(collection)\n if (dek === undefined) continue // no DEK sealed for this collection — skip\n const recs: DecryptedRecord[] = []\n for (const [id, env] of Object.entries(byId)) {\n const plaintext = await openEnvelopeJson(env, dek)\n const body = JSON.parse(plaintext) as Record<string, unknown>\n recs.push({\n id,\n record: { ...body, id },\n ts: env._ts,\n version: env._v,\n ...(env._source !== undefined ? { source: env._source } : {}),\n ...(env._sourceTs !== undefined ? { sourceTs: env._sourceTs } : {}),\n })\n }\n out[collection] = recs\n }\n return out\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-audit/portability/request-withdrawal.ts"],"sourcesContent":["/**\n * Two-party withdrawal ceremony.\n *\n * The conservative counterpart to `unilateralWithdrawal`: a non-owner —\n * including a read-only `client`/`viewer` who cannot self-serve a deletion —\n * files a durable REQUEST; an owner/admin reviews it and either APPROVES\n * (extract-and-dispose under firm authority) or REJECTS it. Every step is\n * audited in the tamper-evident ledger.\n *\n * requester: vault.user.requestWithdrawal({ scope, disposition?, legalBasis? })\n * owner: vault.user.listWithdrawalRequests()\n * vault.user.approveWithdrawal(requestId, { reKey }) → { bundle, snapshot? }\n * vault.user.rejectWithdrawal(requestId, { reason })\n *\n * Requests live in the reserved `_user_withdrawal_requests` namespace. The\n * record body is plaintext metadata (collection names + disposition + legal\n * basis — none secret in this trust model; the owner sees the data anyway) and\n * carries NO passphrase: the re-key passphrase is supplied by the approver at\n * approval time and conveyed to the requester out-of-band, so no secret is\n * stored at rest.\n */\nimport type { Vault } from '../../kernel/vault.js'\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport { NoydbError } from '../../kernel/errors.js'\nimport { resolveAccessibleCollections, buildAccessibleBundle } from './export-accessible.js'\nimport { freezeAndDeleteClosure, randomId, type FrozenSnapshotRef, type WithdrawResult } from './withdraw-accessible.js'\n\nexport const WITHDRAWAL_REQUESTS_COLLECTION = '_user_withdrawal_requests'\n\n/** Raised when a request is missing, already decided, or expired. */\nexport class WithdrawalRequestError extends NoydbError {\n constructor(message: string) {\n super('WITHDRAWAL_REQUEST', message)\n this.name = 'WithdrawalRequestError'\n }\n}\n\nexport type WithdrawalRequestStatus = 'pending' | 'approved' | 'rejected'\n\nexport interface WithdrawalRequest {\n readonly requestId: string\n readonly requester: string\n readonly collections: readonly string[]\n readonly disposition: 'delete' | 'freeze'\n readonly legalBasis?: string\n readonly status: WithdrawalRequestStatus\n readonly requestedAt: string\n readonly expiresAt?: string\n readonly decidedAt?: string\n readonly decidedBy?: string\n readonly rejectReason?: string\n readonly snapshotSha256?: string\n}\n\nexport interface RequestWithdrawalOptions {\n readonly scope?: { readonly collections?: readonly string[] }\n readonly disposition?: 'delete' | 'freeze'\n readonly legalBasis?: string\n /** Time-to-live in ms; after this the request can no longer be approved. */\n readonly expiresInMs?: number\n}\n\nexport interface RequestWithdrawalResult {\n readonly requestId: string\n readonly status: WithdrawalRequestStatus\n readonly expiresAt?: string\n}\n\nfunction writeRequest(vault: Vault, req: WithdrawalRequest, expectedVersion: number): Promise<void> {\n const { name: vaultName, adapter } = vault._introspectState()\n const body = JSON.stringify(req)\n const env: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION, _v: expectedVersion + 1, _ts: req.decidedAt ?? req.requestedAt,\n _iv: '', _data: body, _by: req.decidedBy ?? req.requester,\n }\n return adapter.put(vaultName, WITHDRAWAL_REQUESTS_COLLECTION, req.requestId, env, expectedVersion)\n}\n\nasync function readRequest(vault: Vault, requestId: string): Promise<{ req: WithdrawalRequest; version: number }> {\n const { name: vaultName, adapter } = vault._introspectState()\n const env = await adapter.get(vaultName, WITHDRAWAL_REQUESTS_COLLECTION, requestId)\n if (!env) throw new WithdrawalRequestError(`withdrawal request \"${requestId}\" not found`)\n return { req: JSON.parse(env._data) as WithdrawalRequest, version: env._v }\n}\n\n/**\n * Requester side. Files a durable request for the caller's accessible scope.\n * Gated by `user-request-withdrawal` (checked in the UserApi wrapper).\n */\nexport async function requestWithdrawal(\n vault: Vault,\n opts: RequestWithdrawalOptions = {},\n): Promise<RequestWithdrawalResult> {\n const { keyring } = vault._introspectState()\n // Resolve the requester's accessible collections (read access defines \"theirs\";\n // the owner — who can delete anything — executes the disposition on approval).\n const collections = resolveAccessibleCollections(keyring, opts.scope, false)\n if (!collections || collections.length === 0) {\n throw new WithdrawalRequestError(\n 'requestWithdrawal needs a concrete scope.collections (the caller has all-collection access — name the collections to withdraw)',\n )\n }\n const requestId = `wr-${randomId()}`\n const requestedAt = new Date().toISOString()\n const req: WithdrawalRequest = {\n requestId, requester: keyring.userId, collections, disposition: opts.disposition ?? 'delete',\n status: 'pending', requestedAt,\n ...(opts.legalBasis ? { legalBasis: opts.legalBasis } : {}),\n ...(opts.expiresInMs ? { expiresAt: new Date(Date.parse(requestedAt) + opts.expiresInMs).toISOString() } : {}),\n }\n await writeRequest(vault, req, 0) // create-only\n await vault._getLedgerOrNull()?.append({\n op: 'lifecycle', collection: '', id: '', version: 0, actor: keyring.userId, payloadHash: '',\n reason: `user-withdrawal-request:${requestId}:${keyring.userId}`,\n })\n return { requestId, status: 'pending', ...(req.expiresAt ? { expiresAt: req.expiresAt } : {}) }\n}\n\n/** Owner side. List filed requests (optionally by status). */\nexport async function listWithdrawalRequests(\n vault: Vault,\n opts: { status?: WithdrawalRequestStatus } = {},\n): Promise<WithdrawalRequest[]> {\n const { name: vaultName, adapter } = vault._introspectState()\n const ids = await adapter.list(vaultName, WITHDRAWAL_REQUESTS_COLLECTION)\n const out: WithdrawalRequest[] = []\n for (const id of ids) {\n const env = await adapter.get(vaultName, WITHDRAWAL_REQUESTS_COLLECTION, id)\n if (!env) continue\n const req = JSON.parse(env._data) as WithdrawalRequest\n if (!opts.status || req.status === opts.status) out.push(req)\n }\n return out\n}\n\nfunction assertApprover(vault: Vault): string {\n const { keyring } = vault._introspectState()\n // FR-6: custodian is INTENTIONALLY excluded (SAFER default — review flag).\n // Approving a two-party withdrawal is a destructive extract-and-dispose\n // exercised under FIRM authority — a governance act adjacent to grant/revoke,\n // which a non-owning custodian cannot do. It stays owner/admin-only.\n if (keyring.role !== 'owner' && keyring.role !== 'admin') {\n throw new WithdrawalRequestError('approveWithdrawal / rejectWithdrawal require an owner or admin')\n }\n return keyring.userId\n}\n\nfunction assertPending(req: WithdrawalRequest): void {\n if (req.status !== 'pending') {\n throw new WithdrawalRequestError(`withdrawal request \"${req.requestId}\" is already ${req.status}`)\n }\n if (req.expiresAt && Date.now() > Date.parse(req.expiresAt)) {\n throw new WithdrawalRequestError(`withdrawal request \"${req.requestId}\" expired at ${req.expiresAt}`)\n }\n}\n\nexport interface ApproveWithdrawalOptions {\n /** Re-key the handed-back bundle to a passphrase the requester will use. */\n readonly reKey?: { readonly passphrase: string }\n}\n\n/**\n * Owner side. Extract the requester's recorded scope under firm authority,\n * dispose of the source per the request's disposition, mark the request\n * approved, and return the re-keyed bundle to hand back. Gated by\n * `approve-user-withdrawal` (checked in the UserApi wrapper) + owner/admin role.\n */\nexport async function approveWithdrawal(\n vault: Vault,\n requestId: string,\n opts: ApproveWithdrawalOptions = {},\n): Promise<WithdrawResult> {\n const approver = assertApprover(vault)\n const { req, version } = await readRequest(vault, requestId)\n assertPending(req)\n\n // 1. Produce the requester's re-keyed portable copy FIRST (owner authority\n // holds every DEK, so the recorded collections are all readable).\n const bundle = await buildAccessibleBundle(vault, [...req.collections], opts.reKey)\n\n // 2. + 3. dispose of the source (freeze snapshot, optional → delete-closure).\n const snapshot: FrozenSnapshotRef | undefined = await freezeAndDeleteClosure(vault, req.collections, {\n disposition: req.disposition, actorUserId: approver, withdrawalId: `wd-${requestId}`,\n })\n\n // 4. mark approved + audit (OCC against the version we read).\n const decidedAt = new Date().toISOString()\n await writeRequest(vault, {\n ...req, status: 'approved', decidedAt, decidedBy: approver,\n ...(snapshot ? { snapshotSha256: snapshot.sha256 } : {}),\n }, version)\n await vault._getLedgerOrNull()?.append({\n op: 'lifecycle', collection: '', id: '', version: 0, actor: approver, payloadHash: '',\n reason: `user-withdrawal-approved:${requestId}:${req.requester}:${req.disposition}`,\n })\n\n return snapshot ? { bundle, snapshot } : { bundle }\n}\n\nexport interface RejectWithdrawalOptions {\n readonly reason?: string\n}\n\n/** Owner side. Decline a pending request (no data is touched). */\nexport async function rejectWithdrawal(\n vault: Vault,\n requestId: string,\n opts: RejectWithdrawalOptions = {},\n): Promise<WithdrawalRequest> {\n const approver = assertApprover(vault)\n const { req, version } = await readRequest(vault, requestId)\n assertPending(req)\n const decidedAt = new Date().toISOString()\n const updated: WithdrawalRequest = {\n ...req, status: 'rejected', decidedAt, decidedBy: approver,\n ...(opts.reason ? { rejectReason: opts.reason } : {}),\n }\n await writeRequest(vault, updated, version)\n await vault._getLedgerOrNull()?.append({\n op: 'lifecycle', collection: '', id: '', version: 0, actor: approver, payloadHash: '',\n reason: `user-withdrawal-rejected:${requestId}:${req.requester}`,\n })\n return updated\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4BO,IAAM,iCAAiC;AAGvC,IAAM,yBAAN,cAAqC,WAAW;AAAA,EACrD,YAAY,SAAiB;AAC3B,UAAM,sBAAsB,OAAO;AACnC,SAAK,OAAO;AAAA,EACd;AACF;AAiCA,SAAS,aAAa,OAAc,KAAwB,iBAAwC;AAClG,QAAM,EAAE,MAAM,WAAW,QAAQ,IAAI,MAAM,iBAAiB;AAC5D,QAAM,OAAO,KAAK,UAAU,GAAG;AAC/B,QAAM,MAAyB;AAAA,IAC7B,QAAQ;AAAA,IAAsB,IAAI,kBAAkB;AAAA,IAAG,KAAK,IAAI,aAAa,IAAI;AAAA,IACjF,KAAK;AAAA,IAAI,OAAO;AAAA,IAAM,KAAK,IAAI,aAAa,IAAI;AAAA,EAClD;AACA,SAAO,QAAQ,IAAI,WAAW,gCAAgC,IAAI,WAAW,KAAK,eAAe;AACnG;AAEA,eAAe,YAAY,OAAc,WAAyE;AAChH,QAAM,EAAE,MAAM,WAAW,QAAQ,IAAI,MAAM,iBAAiB;AAC5D,QAAM,MAAM,MAAM,QAAQ,IAAI,WAAW,gCAAgC,SAAS;AAClF,MAAI,CAAC,IAAK,OAAM,IAAI,uBAAuB,uBAAuB,SAAS,aAAa;AACxF,SAAO,EAAE,KAAK,KAAK,MAAM,IAAI,KAAK,GAAwB,SAAS,IAAI,GAAG;AAC5E;AAMA,eAAsB,kBACpB,OACA,OAAiC,CAAC,GACA;AAClC,QAAM,EAAE,QAAQ,IAAI,MAAM,iBAAiB;AAG3C,QAAM,cAAc,6BAA6B,SAAS,KAAK,OAAO,KAAK;AAC3E,MAAI,CAAC,eAAe,YAAY,WAAW,GAAG;AAC5C,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,YAAY,MAAM,SAAS,CAAC;AAClC,QAAM,eAAc,oBAAI,KAAK,GAAE,YAAY;AAC3C,QAAM,MAAyB;AAAA,IAC7B;AAAA,IAAW,WAAW,QAAQ;AAAA,IAAQ;AAAA,IAAa,aAAa,KAAK,eAAe;AAAA,IACpF,QAAQ;AAAA,IAAW;AAAA,IACnB,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,IACzD,GAAI,KAAK,cAAc,EAAE,WAAW,IAAI,KAAK,KAAK,MAAM,WAAW,IAAI,KAAK,WAAW,EAAE,YAAY,EAAE,IAAI,CAAC;AAAA,EAC9G;AACA,QAAM,aAAa,OAAO,KAAK,CAAC;AAChC,QAAM,MAAM,iBAAiB,GAAG,OAAO;AAAA,IACrC,IAAI;AAAA,IAAa,YAAY;AAAA,IAAI,IAAI;AAAA,IAAI,SAAS;AAAA,IAAG,OAAO,QAAQ;AAAA,IAAQ,aAAa;AAAA,IACzF,QAAQ,2BAA2B,SAAS,IAAI,QAAQ,MAAM;AAAA,EAChE,CAAC;AACD,SAAO,EAAE,WAAW,QAAQ,WAAW,GAAI,IAAI,YAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC,EAAG;AAChG;AAGA,eAAsB,uBACpB,OACA,OAA6C,CAAC,GAChB;AAC9B,QAAM,EAAE,MAAM,WAAW,QAAQ,IAAI,MAAM,iBAAiB;AAC5D,QAAM,MAAM,MAAM,QAAQ,KAAK,WAAW,8BAA8B;AACxE,QAAM,MAA2B,CAAC;AAClC,aAAW,MAAM,KAAK;AACpB,UAAM,MAAM,MAAM,QAAQ,IAAI,WAAW,gCAAgC,EAAE;AAC3E,QAAI,CAAC,IAAK;AACV,UAAM,MAAM,KAAK,MAAM,IAAI,KAAK;AAChC,QAAI,CAAC,KAAK,UAAU,IAAI,WAAW,KAAK,OAAQ,KAAI,KAAK,GAAG;AAAA,EAC9D;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAsB;AAC5C,QAAM,EAAE,QAAQ,IAAI,MAAM,iBAAiB;AAK3C,MAAI,QAAQ,SAAS,WAAW,QAAQ,SAAS,SAAS;AACxD,UAAM,IAAI,uBAAuB,gEAAgE;AAAA,EACnG;AACA,SAAO,QAAQ;AACjB;AAEA,SAAS,cAAc,KAA8B;AACnD,MAAI,IAAI,WAAW,WAAW;AAC5B,UAAM,IAAI,uBAAuB,uBAAuB,IAAI,SAAS,gBAAgB,IAAI,MAAM,EAAE;AAAA,EACnG;AACA,MAAI,IAAI,aAAa,KAAK,IAAI,IAAI,KAAK,MAAM,IAAI,SAAS,GAAG;AAC3D,UAAM,IAAI,uBAAuB,uBAAuB,IAAI,SAAS,gBAAgB,IAAI,SAAS,EAAE;AAAA,EACtG;AACF;AAaA,eAAsB,kBACpB,OACA,WACA,OAAiC,CAAC,GACT;AACzB,QAAM,WAAW,eAAe,KAAK;AACrC,QAAM,EAAE,KAAK,QAAQ,IAAI,MAAM,YAAY,OAAO,SAAS;AAC3D,gBAAc,GAAG;AAIjB,QAAM,SAAS,MAAM,sBAAsB,OAAO,CAAC,GAAG,IAAI,WAAW,GAAG,KAAK,KAAK;AAGlF,QAAM,WAA0C,MAAM,uBAAuB,OAAO,IAAI,aAAa;AAAA,IACnG,aAAa,IAAI;AAAA,IAAa,aAAa;AAAA,IAAU,cAAc,MAAM,SAAS;AAAA,EACpF,CAAC;AAGD,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,aAAa,OAAO;AAAA,IACxB,GAAG;AAAA,IAAK,QAAQ;AAAA,IAAY;AAAA,IAAW,WAAW;AAAA,IAClD,GAAI,WAAW,EAAE,gBAAgB,SAAS,OAAO,IAAI,CAAC;AAAA,EACxD,GAAG,OAAO;AACV,QAAM,MAAM,iBAAiB,GAAG,OAAO;AAAA,IACrC,IAAI;AAAA,IAAa,YAAY;AAAA,IAAI,IAAI;AAAA,IAAI,SAAS;AAAA,IAAG,OAAO;AAAA,IAAU,aAAa;AAAA,IACnF,QAAQ,4BAA4B,SAAS,IAAI,IAAI,SAAS,IAAI,IAAI,WAAW;AAAA,EACnF,CAAC;AAED,SAAO,WAAW,EAAE,QAAQ,SAAS,IAAI,EAAE,OAAO;AACpD;AAOA,eAAsB,iBACpB,OACA,WACA,OAAgC,CAAC,GACL;AAC5B,QAAM,WAAW,eAAe,KAAK;AACrC,QAAM,EAAE,KAAK,QAAQ,IAAI,MAAM,YAAY,OAAO,SAAS;AAC3D,gBAAc,GAAG;AACjB,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,UAA6B;AAAA,IACjC,GAAG;AAAA,IAAK,QAAQ;AAAA,IAAY;AAAA,IAAW,WAAW;AAAA,IAClD,GAAI,KAAK,SAAS,EAAE,cAAc,KAAK,OAAO,IAAI,CAAC;AAAA,EACrD;AACA,QAAM,aAAa,OAAO,SAAS,OAAO;AAC1C,QAAM,MAAM,iBAAiB,GAAG,OAAO;AAAA,IACrC,IAAI;AAAA,IAAa,YAAY;AAAA,IAAI,IAAI;AAAA,IAAI,SAAS;AAAA,IAAG,OAAO;AAAA,IAAU,aAAa;AAAA,IACnF,QAAQ,4BAA4B,SAAS,IAAI,IAAI,SAAS;AAAA,EAChE,CAAC;AACD,SAAO;AACT;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-party/session/session.ts","../src/with-party/session/session-policy.ts","../src/with-party/session/dev-unlock.ts"],"sourcesContent":["/**\n * Session tokens —\n *\n * After a vault is unlocked (via passphrase, WebAuthn, OIDC, or magic-\n * link), the caller can call `createSession()` to get a session token that\n * allows re-establishing the KEK for the session lifetime without re-running\n * PBKDF2 or any interactive auth challenge.\n *\n * Security model\n * ──────────────\n * A session consists of two pieces that must both be present to recover the\n * KEK:\n *\n * 1. The **session key** — a non-extractable AES-256-GCM CryptoKey that\n * exists only in memory. \"Non-extractable\" is enforced by the WebCrypto\n * API: the key object cannot be serialized, exported, or sent over\n * postMessage. When the JS context is GC'd (tab close, navigation away,\n * worker termination) the key becomes unrecoverable.\n *\n * 2. The **session token** — a JSON object that carries the KEK wrapped\n * with the session key (AES-256-GCM, fresh IV per session), plus\n * unencrypted session metadata (sessionId, userId, vault, role,\n * expiresAt). The token can be serialized to JSON and stored in\n * sessionStorage or passed across callsites within the same tab, but\n * it is useless without the session key.\n *\n * The session key is kept in a module-level Map indexed by sessionId. Callers\n * that need to re-use a session must hold on to the sessionId returned from\n * `createSession()`; the key is looked up automatically by `resolveSession()`.\n *\n * Revocation: `revokeSession()` removes the entry from the Map. Because the\n * key is non-extractable, removal is sufficient — no one holds a serializable\n * copy of the key.\n *\n * Tab-scoped lifetime: the module-level Map lives only as long as the JS\n * module. Tab close → module unloaded → Map GC'd → all session keys gone.\n * This is the zero-effort logout: closing the tab is always a secure logout.\n *\n * Expiry: `createSession()` accepts a `ttlMs` option. `resolveSession()`\n * checks `expiresAt` and throws `SessionExpiredError` if the token is stale,\n * even if the session key is still in the Map.\n */\n\nimport { bufferToBase64, base64ToBuffer, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { generateULID } from '../../with-pod/ulid.js'\nimport type { Role } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from '../team/keyring.js'\nimport { SessionExpiredError, SessionNotFoundError } from '../../kernel/errors.js'\n\nconst subtle = globalThis.crypto.subtle\n\n// Default session TTL: 60 minutes\nconst DEFAULT_TTL_MS = 60 * 60 * 1000\n\n// Module-level session key store. Tab-scoped by construction.\nconst sessionKeyStore = new Map<string, CryptoKey>()\n\n// ─── Public types ──────────────────────────────────────────────────────\n\n/** The serializable part of a session token. Safe to store in sessionStorage. */\nexport interface SessionToken {\n readonly _noydb_session: 1\n /** Unique session identifier (ULID). Use this as the handle for resolve/revoke. */\n readonly sessionId: string\n readonly userId: string\n readonly vault: string\n readonly role: Role\n /** ISO timestamp — resolveSession() rejects this token after this time. */\n readonly expiresAt: string\n /** KEK wrapped with the session key (AES-256-GCM). Base64. */\n readonly wrappedKek: string\n /** IV used for the wrapping operation. Base64. */\n readonly kekIv: string\n}\n\n/** Result returned from `createSession()`. */\nexport interface CreateSessionResult {\n /** Serializable token — store in sessionStorage or pass to `resolveSession()`. */\n token: SessionToken\n /** The sessionId — use this handle for `resolveSession()` and `revokeSession()`. */\n sessionId: string\n}\n\n/** Options for `createSession()`. */\nexport interface CreateSessionOptions {\n /**\n * Session lifetime in milliseconds. Defaults to 60 minutes.\n * After this duration, `resolveSession()` throws `SessionExpiredError`.\n */\n ttlMs?: number\n}\n\n// ─── Core session operations ───────────────────────────────────────────\n\n/**\n * Create a session for an already-unlocked keyring.\n *\n * Call this after any successful unlock (passphrase, WebAuthn, OIDC,\n * magic-link). The returned `sessionId` is the handle for later\n * `resolveSession()` and `revokeSession()` calls.\n *\n * The session key is generated fresh (non-extractable) and stored in the\n * module-level Map. The KEK from `keyring.kek` is exported (it must be\n * extractable — it was derived by `deriveKey()` which sets extractable: false,\n * but it's unwrapped from the keyring which sets extractable: true) and then\n * re-wrapped with the session key.\n *\n * @param keyring - An already-unlocked keyring whose `kek` is available.\n * @param vault - The vault name this session is scoped to.\n * @param options - Optional session configuration.\n */\nexport async function createSession(\n keyring: UnlockedKeyring,\n vault: string,\n options: CreateSessionOptions = {},\n): Promise<CreateSessionResult> {\n const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS\n const sessionId = generateULID()\n const expiresAt = new Date(Date.now() + ttlMs).toISOString()\n\n // Generate a fresh non-extractable session key.\n // AES-256-GCM is used here (rather than AES-KW) because the session key\n // wraps raw key bytes (the exported KEK) rather than a CryptoKey object.\n const sessionKey = await subtle.generateKey(\n { name: 'AES-GCM', length: 256 },\n false, // non-extractable — this is the tab-scope security invariant\n ['encrypt', 'decrypt'],\n )\n\n // Export the KEK as raw bytes so we can wrap it.\n // The KEK is AES-256-KW, which must have been importable (extractable: true)\n // to allow wrapKey — it is, because unwrapKey sets extractable: true for\n // DEKs, but the KEK itself is derived with extractable: false (see\n // crypto.ts deriveKey). We use a separate raw export + encrypt path.\n //\n // Wait — the KEK is AES-KW with extractable:false. We cannot export it.\n // Instead, we wrap the DEKs (which ARE extractable) and the salt+role+userId\n // metadata together. This means resolveSession() reconstructs an\n // UnlockedKeyring by re-wrapping the DEKs list from the token.\n //\n // Simpler approach: export each DEK (they're extractable) and encrypt\n // the serialized DEK map with the session key. The keyring is reconstructed\n // from the session token without the original KEK — only DEKs matter for\n // record operations.\n //\n // This is the right design: sessions don't need the KEK (no re-grant,\n // no re-derive during session lifetime). They need the DEK set.\n\n const dekMap: Record<string, string> = {}\n for (const [collName, dek] of keyring.deks) {\n const raw = await subtle.exportKey('raw', dek)\n dekMap[collName] = bufferToBase64(raw)\n }\n\n const payload = JSON.stringify({\n userId: keyring.userId,\n displayName: keyring.displayName,\n role: keyring.role,\n permissions: keyring.permissions,\n deks: dekMap,\n salt: bufferToBase64(keyring.salt),\n })\n\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12))\n const encrypted = await subtle.encrypt(\n { name: 'AES-GCM', iv },\n sessionKey,\n new TextEncoder().encode(payload),\n )\n\n const token: SessionToken = {\n _noydb_session: 1,\n sessionId,\n userId: keyring.userId,\n vault,\n role: keyring.role,\n expiresAt,\n wrappedKek: bufferToBase64(encrypted),\n kekIv: bufferToBase64(iv),\n }\n\n sessionKeyStore.set(sessionId, sessionKey)\n return { token, sessionId }\n}\n\n/**\n * Resolve a session token back into an UnlockedKeyring.\n *\n * Looks up the session key by `sessionId`, checks the token is not expired,\n * then decrypts the payload to reconstruct the keyring's DEK set.\n *\n * Throws `SessionExpiredError` if the token's `expiresAt` is in the past.\n * Throws `SessionNotFoundError` if the session key is not in the store\n * (tab was reloaded, session was revoked, or the sessionId is wrong).\n *\n * @param token - The SessionToken from `createSession()`.\n */\nexport async function resolveSession(token: SessionToken): Promise<UnlockedKeyring> {\n // Expiry check first — fast path without touching crypto\n if (Date.now() > new Date(token.expiresAt).getTime()) {\n sessionKeyStore.delete(token.sessionId)\n throw new SessionExpiredError(token.sessionId)\n }\n\n const sessionKey = sessionKeyStore.get(token.sessionId)\n if (!sessionKey) {\n throw new SessionNotFoundError(token.sessionId)\n }\n\n const iv = base64ToBuffer(token.kekIv)\n const ciphertext = base64ToBuffer(token.wrappedKek)\n\n let plaintext: ArrayBuffer\n try {\n plaintext = await subtle.decrypt(\n { name: 'AES-GCM', iv },\n sessionKey,\n ciphertext,\n )\n } catch {\n throw new SessionNotFoundError(token.sessionId)\n }\n\n const payload = JSON.parse(new TextDecoder().decode(plaintext)) as {\n userId: string\n displayName: string\n role: Role\n permissions: Record<string, 'rw' | 'ro'>\n deks: Record<string, string>\n salt: string\n }\n\n const deks = new Map<string, EnclaveKey>()\n for (const [collName, rawBase64] of Object.entries(payload.deks)) {\n const dek = await subtle.importKey(\n 'raw',\n base64ToBuffer(rawBase64),\n { name: 'AES-GCM', length: 256 },\n true,\n ['encrypt', 'decrypt'],\n )\n deks.set(collName, dek)\n }\n\n return {\n userId: payload.userId,\n displayName: payload.displayName,\n role: payload.role,\n permissions: payload.permissions,\n deks,\n kek: null, // KEK not available in session context\n salt: base64ToBuffer(payload.salt),\n authenticators: [],\n }\n}\n\n/**\n * Revoke a session by removing its key from the store.\n *\n * After revocation, `resolveSession()` will throw `SessionNotFoundError`\n * for this sessionId. The session token (if held by the caller) becomes\n * permanently useless. This is the explicit logout path.\n *\n * No-op if the session was already expired or does not exist.\n */\nexport function revokeSession(sessionId: string): void {\n sessionKeyStore.delete(sessionId)\n}\n\n/**\n * Check if a session is still alive (key in store + not expired).\n * Does not decrypt anything — purely a metadata check.\n */\nexport function isSessionAlive(token: SessionToken): boolean {\n if (Date.now() > new Date(token.expiresAt).getTime()) return false\n return sessionKeyStore.has(token.sessionId)\n}\n\n/**\n * Revoke all active sessions. Used by `Noydb.close()` to ensure that\n * closing the instance destroys all session state, not just the keyring\n * cache.\n */\nexport function revokeAllSessions(): void {\n sessionKeyStore.clear()\n}\n\n/**\n * Return the number of active sessions currently in the store.\n * Useful for diagnostics and tests.\n */\nexport function activeSessionCount(): number {\n return sessionKeyStore.size\n}\n","/**\n * Session policies —\n *\n * A `SessionPolicy` is a small declarative object that controls how long a\n * session lives and which operations require re-authentication. It is\n * evaluated by the `PolicyEnforcer` class, which the Noydb instance\n * integrates to replace the bare `sessionTimeout` timer from.\n *\n * Design decisions\n * ────────────────\n * Policies are stateless value objects — no timers, no event listeners.\n * The Noydb instance is the stateful coordinator: it holds the enforcer,\n * calls `enforcer.touch()` on every operation, and calls\n * `enforcer.checkOperation()` before high-risk operations.\n *\n * This keeps the policy module easy to unit-test (no global timers to mock)\n * and avoids the \"who owns cleanup\" problem that comes with timer-based\n * callbacks embedded in a value object.\n *\n * `lockOnBackground` registers a `visibilitychange` listener on the document\n * at enforcer creation time and removes it on `destroy()`. It is a no-op in\n * non-browser environments (no `document`).\n */\n\nimport type { SessionPolicy, ReAuthOperation } from '../../kernel/types.js'\nimport { SessionExpiredError, SessionPolicyError } from '../../kernel/errors.js'\nimport { revokeSession } from './session.js'\n\n// ─── PolicyEnforcer ────────────────────────────────────────────────────\n\nexport interface PolicyEnforcerOptions {\n /** The policy to enforce. */\n policy: SessionPolicy\n /** The session ID to revoke when idle/absolute timeouts fire. */\n sessionId: string\n /**\n * Called when the policy decides the session should end (idle timeout,\n * absolute timeout, or lockOnBackground). Use this to trigger the\n * same cleanup that `Noydb.close()` would perform.\n */\n onRevoke: (reason: 'idle' | 'absolute' | 'background') => void\n}\n\n/**\n * Stateful enforcer for a single session policy.\n *\n * Create one per open session, call `touch()` on every operation,\n * call `checkOperation(op)` before export/grant/revoke/rotate/changeSecret,\n * and call `destroy()` when the session ends.\n */\nexport class PolicyEnforcer {\n private readonly policy: SessionPolicy\n private readonly sessionId: string\n private readonly onRevoke: PolicyEnforcerOptions['onRevoke']\n private readonly createdAt: number\n private lastActivityAt: number\n private idleTimer: ReturnType<typeof setTimeout> | null = null\n private absoluteTimer: ReturnType<typeof setTimeout> | null = null\n private visibilityHandler: (() => void) | null = null\n\n constructor(opts: PolicyEnforcerOptions) {\n this.policy = opts.policy\n this.sessionId = opts.sessionId\n this.onRevoke = opts.onRevoke\n this.createdAt = Date.now()\n this.lastActivityAt = Date.now()\n\n this.scheduleIdleTimer()\n this.scheduleAbsoluteTimer()\n this.registerBackgroundLock()\n }\n\n /**\n * Record an activity timestamp and reset the idle timer.\n * Call this at the top of every Noydb public method.\n */\n touch(): void {\n this.lastActivityAt = Date.now()\n this.scheduleIdleTimer()\n }\n\n /**\n * Check whether the given operation is allowed under the active policy.\n * Throws `SessionPolicyError` if the operation requires re-authentication.\n * Throws `SessionExpiredError` if the absolute timeout has been exceeded\n * (defensive check in case the timer fired before the call arrived).\n *\n * This is a synchronous check — callers don't await it.\n */\n checkOperation(op: ReAuthOperation): void {\n // Defensive absolute-timeout check (timer may have fired late)\n const { absoluteTimeoutMs } = this.policy\n if (absoluteTimeoutMs !== undefined && Date.now() - this.createdAt >= absoluteTimeoutMs) {\n this.expire('absolute')\n throw new SessionExpiredError(this.sessionId)\n }\n\n const required = this.policy.requireReAuthFor ?? []\n if (required.includes(op)) {\n throw new SessionPolicyError(op)\n }\n }\n\n /**\n * Tear down timers and background-lock listener. Call from `Noydb.close()`\n * and whenever the session is revoked externally.\n */\n destroy(): void {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = null\n }\n if (this.absoluteTimer) {\n clearTimeout(this.absoluteTimer)\n this.absoluteTimer = null\n }\n if (this.visibilityHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.visibilityHandler)\n this.visibilityHandler = null\n }\n }\n\n /** How long since the last activity, in ms. */\n get idleMs(): number {\n return Date.now() - this.lastActivityAt\n }\n\n /** How long since session creation, in ms. */\n get ageMs(): number {\n return Date.now() - this.createdAt\n }\n\n // ── Private ──────────────────────────────────────────────────────────\n\n private scheduleIdleTimer(): void {\n const { idleTimeoutMs } = this.policy\n if (!idleTimeoutMs) return\n\n if (this.idleTimer) clearTimeout(this.idleTimer)\n this.idleTimer = setTimeout(() => {\n this.expire('idle')\n }, idleTimeoutMs)\n }\n\n private scheduleAbsoluteTimer(): void {\n const { absoluteTimeoutMs } = this.policy\n if (!absoluteTimeoutMs) return\n\n if (this.absoluteTimer) clearTimeout(this.absoluteTimer)\n this.absoluteTimer = setTimeout(() => {\n this.expire('absolute')\n }, absoluteTimeoutMs)\n }\n\n private registerBackgroundLock(): void {\n if (!this.policy.lockOnBackground) return\n if (typeof document === 'undefined') return\n\n this.visibilityHandler = () => {\n if (document.hidden) {\n this.expire('background')\n }\n }\n document.addEventListener('visibilitychange', this.visibilityHandler)\n }\n\n private expire(reason: 'idle' | 'absolute' | 'background'): void {\n this.destroy()\n revokeSession(this.sessionId)\n this.onRevoke(reason)\n }\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────\n\n/**\n * Build a `PolicyEnforcer` from a policy + session token, and return it\n * alongside a cleanup function. Convenience wrapper for Noydb.\n */\nexport function createEnforcer(opts: PolicyEnforcerOptions): PolicyEnforcer {\n return new PolicyEnforcer(opts)\n}\n\n/**\n * Validate that a `SessionPolicy` is well-formed.\n * Throws a plain `Error` (not `NoydbError`) because this is a developer\n * error — invalid policies passed at construction time, not at runtime.\n */\nexport function validateSessionPolicy(policy: SessionPolicy): void {\n const { idleTimeoutMs, absoluteTimeoutMs } = policy\n if (idleTimeoutMs !== undefined && (typeof idleTimeoutMs !== 'number' || idleTimeoutMs <= 0)) {\n throw new Error(`SessionPolicy.idleTimeoutMs must be a positive number, got ${idleTimeoutMs}`)\n }\n if (absoluteTimeoutMs !== undefined && (typeof absoluteTimeoutMs !== 'number' || absoluteTimeoutMs <= 0)) {\n throw new Error(`SessionPolicy.absoluteTimeoutMs must be a positive number, got ${absoluteTimeoutMs}`)\n }\n if (idleTimeoutMs !== undefined && absoluteTimeoutMs !== undefined && idleTimeoutMs >= absoluteTimeoutMs) {\n throw new Error(\n `SessionPolicy.idleTimeoutMs (${idleTimeoutMs}ms) must be less than absoluteTimeoutMs (${absoluteTimeoutMs}ms)`,\n )\n }\n}\n","/**\n * Dev-mode persistent unlock —\n *\n * Solves the developer inner-loop friction: hot-reload destroys the session\n * (page navigation semantics), forcing a passphrase re-entry every refresh.\n *\n * This module provides an opt-in, deliberately-named escape hatch that lets\n * developers store the keyring payload in sessionStorage or localStorage so\n * the vault auto-unlocks on every page load — without a passphrase,\n * without a biometric prompt, without any OIDC flow.\n *\n * ⚠️ WARNING — this is a loaded footgun ⚠️\n * ─────────────────────────────────────────\n * The keyring payload stored by this module contains the DEKs. Whoever has\n * access to sessionStorage/localStorage has access to the DEKs. On a shared\n * development machine, a compromised browser extension, or a mis-configured\n * origin, this is a complete key exposure.\n *\n * This module is ONLY safe for local development. It must NEVER be active\n * in production builds.\n *\n * Guardrails (all enforced by the module, not by the caller)\n * ──────────────────────────────────────────────────────────\n * 1. **Production guard:** `enableDevUnlock()` throws immediately if\n * `process.env.NODE_ENV === 'production'` or if `import.meta.env?.PROD === true`\n * (Vite convention). Also throws if the hostname is NOT localhost or 127.0.0.1.\n *\n * 2. **Explicit acknowledgement string:** the caller must pass\n * `acknowledge: 'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY'` or the call\n * throws. This string appears in every grep for `devUnlock` in the codebase,\n * making it impossible to enable this feature accidentally.\n *\n * 3. **Scope is vault + userId:** the storage key includes both the\n * vault name and the userId, so dev-unlock for vault-A does\n * NOT auto-unlock vault-B.\n *\n * 4. **Storage scope:** default is `sessionStorage` (cleared on tab close).\n * `localStorage` is opt-in and requires an additional\n * `persistAcrossTabs: true` flag in the options.\n *\n * 5. **Clear method:** `clearDevUnlock()` removes the stored payload. Wire\n * this to a dev toolbar button or `Ctrl+Shift+L` so clearing is one action.\n *\n * 6. **Console banner:** on first enable, a highly visible console warning\n * fires. Cannot be suppressed.\n *\n * Usage\n * ─────\n * ```ts\n * // In your dev entry point only (guarded by import.meta.env.DEV):\n * if (import.meta.env.DEV) {\n * const { enableDevUnlock, loadDevUnlock } = await import('@noy-db/hub')\n * enableDevUnlock('my-compartment', 'alice', keyring, {\n * acknowledge: 'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY',\n * })\n * }\n *\n * // On page load:\n * if (import.meta.env.DEV) {\n * const keyring = await loadDevUnlock('my-compartment', 'alice')\n * if (keyring) {\n * // Skip unlock prompt, use keyring directly\n * }\n * }\n * ```\n */\n\nimport { bufferToBase64, base64ToBuffer, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { ValidationError } from '../../kernel/errors.js'\nimport type { UnlockedKeyring } from '../team/keyring.js'\nimport type { Role } from '../../kernel/types.js'\n\n// The exact acknowledgement string callers must pass\nconst REQUIRED_ACKNOWLEDGE = 'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY'\n\nconst STORAGE_PREFIX = 'noydb:dev-unlock:'\n\n// ─── Options ──────────────────────────────────────────────────────────\n\nexport interface DevUnlockOptions {\n /**\n * Required: the exact string 'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY'.\n * Any other value causes `enableDevUnlock()` to throw.\n */\n acknowledge: string\n /**\n * If `true`, stores in localStorage (persists across tabs and browser restarts).\n * If `false` (default), stores in sessionStorage (cleared on tab close).\n */\n persistAcrossTabs?: boolean\n}\n\n// ─── Production guard ─────────────────────────────────────────────────\n\nfunction assertDevEnvironment(): void {\n // Node.js: check NODE_ENV\n if (\n typeof process !== 'undefined' &&\n process.env.NODE_ENV === 'production'\n ) {\n throw new ValidationError(\n 'devUnlock is not available in production builds. ' +\n 'process.env.NODE_ENV is \"production\".',\n )\n }\n\n // Vite / build tool convention\n if (\n typeof globalThis !== 'undefined' &&\n (globalThis as Record<string, unknown>).__vite_is_production__ === true\n ) {\n throw new ValidationError('devUnlock is not available in production builds.')\n }\n\n // Browser: only allow on localhost\n if (\n typeof window !== 'undefined' &&\n typeof window.location !== 'undefined'\n ) {\n const host = window.location.hostname\n if (host !== 'localhost' && host !== '127.0.0.1' && host !== '::1' && !host.endsWith('.local')) {\n throw new ValidationError(\n `devUnlock is only available on localhost. Current hostname: \"${host}\". ` +\n 'Set NODE_ENV=development and run on localhost to use dev unlock.',\n )\n }\n }\n}\n\n// ─── Storage key ──────────────────────────────────────────────────────\n\nfunction storageKey(vault: string, userId: string): string {\n return `${STORAGE_PREFIX}${vault}:${userId}`\n}\n\nfunction resolveStorage(persistAcrossTabs?: boolean): Storage {\n if (typeof window === 'undefined') {\n throw new ValidationError('devUnlock requires a browser environment (window.sessionStorage / window.localStorage).')\n }\n return persistAcrossTabs ? window.localStorage : window.sessionStorage\n}\n\n// ─── Public API ────────────────────────────────────────────────────────\n\n/**\n * Serialize and store a keyring to browser storage for dev-mode auto-unlock.\n *\n * Throws immediately if:\n * - The acknowledge string is wrong.\n * - Running in a production environment (NODE_ENV=production).\n * - Running on a non-localhost hostname.\n *\n * Emits a highly visible console warning that cannot be suppressed.\n *\n * @param vault - The vault name.\n * @param userId - The user ID.\n * @param keyring - The unlocked keyring to persist.\n * @param options - Options including the required acknowledge string.\n */\nexport async function enableDevUnlock(\n vault: string,\n userId: string,\n keyring: UnlockedKeyring,\n options: DevUnlockOptions,\n): Promise<void> {\n if (options.acknowledge !== REQUIRED_ACKNOWLEDGE) {\n throw new ValidationError(\n `devUnlock requires acknowledge: '${REQUIRED_ACKNOWLEDGE}'. ` +\n `Got: '${options.acknowledge}'. This is intentional — the full string must appear in your source.`,\n )\n }\n\n assertDevEnvironment()\n\n const storage = resolveStorage(options.persistAcrossTabs)\n\n const dekMap: Record<string, string> = {}\n for (const [collName, dek] of keyring.deks) {\n const raw = await globalThis.crypto.subtle.exportKey('raw', dek)\n dekMap[collName] = bufferToBase64(raw)\n }\n\n const payload = JSON.stringify({\n _noydb_dev_unlock: 1,\n userId: keyring.userId,\n displayName: keyring.displayName,\n role: keyring.role,\n permissions: keyring.permissions,\n deks: dekMap,\n salt: bufferToBase64(keyring.salt),\n })\n\n storage.setItem(storageKey(vault, userId), payload)\n\n // Visible, unsuppressable warning\n console.warn(\n '%c⚠️ NOYDB DEV UNLOCK ACTIVE ⚠️',\n 'color: red; font-size: 16px; font-weight: bold',\n `\\n\\nVault \"${vault}\" user \"${userId}\" is stored in ` +\n `${options.persistAcrossTabs ? 'localStorage' : 'sessionStorage'} in PLAINTEXT DEKs.\\n` +\n 'This is ONLY safe for local development. Never use in production.\\n' +\n 'Call clearDevUnlock() to remove.',\n )\n}\n\n/**\n * Load a dev-mode keyring from browser storage.\n *\n * Returns `null` if no dev-unlock state is stored for this vault + user,\n * or if the stored payload is malformed.\n *\n * Does NOT perform the production environment check — it's safe to CALL\n * `loadDevUnlock` in production (it will simply return `null` because no\n * dev-unlock state was ever written). The guard only fires on `enableDevUnlock`.\n *\n * @param vault - The vault name.\n * @param userId - The user ID.\n * @param options - Optional storage override.\n */\nexport async function loadDevUnlock(\n vault: string,\n userId: string,\n options: { persistAcrossTabs?: boolean } = {},\n): Promise<UnlockedKeyring | null> {\n if (typeof window === 'undefined') return null\n\n const storage = resolveStorage(options.persistAcrossTabs)\n const raw = storage.getItem(storageKey(vault, userId))\n if (!raw) return null\n\n let parsed: {\n _noydb_dev_unlock?: number\n userId: string\n displayName: string\n role: Role\n permissions: Record<string, 'rw' | 'ro'>\n deks: Record<string, string>\n salt: string\n }\n try {\n parsed = JSON.parse(raw)\n } catch {\n return null\n }\n\n if (parsed._noydb_dev_unlock !== 1) return null\n\n const deks = new Map<string, EnclaveKey>()\n for (const [collName, rawBase64] of Object.entries(parsed.deks)) {\n const dek = await globalThis.crypto.subtle.importKey(\n 'raw',\n base64ToBuffer(rawBase64),\n { name: 'AES-GCM', length: 256 },\n true,\n ['encrypt', 'decrypt'],\n )\n deks.set(collName, dek)\n }\n\n return {\n userId: parsed.userId,\n displayName: parsed.displayName,\n role: parsed.role,\n permissions: parsed.permissions,\n deks,\n kek: null,\n salt: base64ToBuffer(parsed.salt),\n authenticators: [],\n }\n}\n\n/**\n * Remove dev-unlock state from browser storage.\n *\n * Safe to call in production (no-op if no dev state exists).\n */\nexport function clearDevUnlock(\n vault: string,\n userId: string,\n options: { persistAcrossTabs?: boolean } = {},\n): void {\n if (typeof window === 'undefined') return\n const storage = resolveStorage(options.persistAcrossTabs)\n storage.removeItem(storageKey(vault, userId))\n}\n\n/**\n * Check if dev-unlock state exists for this vault + user.\n *\n * Safe to call in production (returns false if nothing is stored).\n */\nexport function isDevUnlockActive(\n vault: string,\n userId: string,\n options: { persistAcrossTabs?: boolean } = {},\n): boolean {\n if (typeof window === 'undefined') return false\n const storage = resolveStorage(options.persistAcrossTabs)\n return storage.getItem(storageKey(vault, userId)) !== null\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-party/team/deed.ts","../src/with-party/custody/liberate.ts"],"sourcesContent":["/**\n * FR-6 — Deed: sealed / hidden-owner provisioning.\n *\n * A **Deed** is a vault owned by a *latent* principal who never\n * authenticates. The owner credential (the random passphrase that\n * derives `KEK_owner`) is minted machine-side and sealed under a\n * **non-firm** {@link SealingKeyProvider} — that sealing boundary is\n * the cryptographic *inalienability anchor*: whoever holds the\n * provider (the client / their KMS) is the only party that can ever\n * re-derive `KEK_owner`. A firm-side custodian operates the vault\n * fully but can never reach the owner key — there is deliberately NO\n * firm-controlled fallback.\n *\n * Provisioning reuses the managed-mode seam:\n * - {@link resolveManagedSecret} mints + seals + persists the random\n * passphrase at `_meta/sealed-passphrase` (and unseals it on every\n * subsequent open through the same provider).\n * - {@link createOwnerKeyring} derives `KEK_owner` from that\n * passphrase and writes the `_keyring/<ownerUserId>` file.\n *\n * On top of that, a Deed writes a `_meta/deed` **marker**. The marker\n * is PLAINTEXT metadata — it records WHO the latent owner is and WHICH\n * sealing boundary protects them, so it must be readable without\n * unlocking the vault (a custodian or auditor inspecting the vault must\n * be able to see \"this is a Deed vault, owner = X, sealed under Y\"\n * without any key). The marker itself is therefore never sealed; only\n * the owner *credential* is.\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { createOwnerKeyring } from './keyring.js'\nimport type { SealingKeyProvider } from './managed-passphrase.js'\nimport { resolveManagedSecret } from './managed-passphrase.js'\n\n/** Reserved id for the Deed marker under `_meta`. */\nexport const DEED_RECORD_ID = 'deed' as const\n\n/**\n * Plaintext metadata recording a vault's latent-owner Deed. Persisted\n * at `_meta/deed`. Readable without unlocking the vault by design — it\n * carries no secret material, only the identity of the latent owner\n * and the sealing boundary that anchors inalienability.\n */\nexport interface DeedMarker {\n /** The latent owner principal — the user id of the `_keyring/<id>` owner file. */\n readonly ownerUserId: string\n /**\n * The `SealingKeyProvider.id` the owner credential is sealed under.\n * The inalienability anchor: only the holder of this (non-firm)\n * provider can re-derive `KEK_owner`. Audit metadata — never secret.\n */\n readonly sealedUnder: string\n /** Always `true` for a Deed — the owner never authenticates interactively. */\n readonly latent: true\n /** ISO-8601 timestamp the Deed was issued. */\n readonly issuedAt: string\n /**\n * ISO-8601 timestamp the vault was liberated (a new owner claimed it\n * via the audited Liberate ceremony), if any. Absent until liberation.\n */\n readonly liberatedAt?: string\n}\n\n/**\n * Wire-format payload stored inside the `_meta/deed` envelope. Carries\n * a magic marker for forensics + the {@link DeedMarker} fields. JSON,\n * AES-GCM bypassed (the marker is plaintext metadata by design).\n */\ninterface DeedEnvelopePayload extends DeedMarker {\n readonly _noydb_deed: 1\n}\n\n/**\n * Provision a Deed: a latent owner whose credential is sealed under\n * `sealing` (a NON-firm provider). Mints + seals the owner passphrase\n * via {@link resolveManagedSecret}, derives the owner keyring via\n * {@link createOwnerKeyring}, then writes the plaintext `_meta/deed`\n * marker. Returns the unlocked owner keyring.\n *\n * The returned keyring is the only in-memory window onto `KEK_owner`\n * for this call; the persistent re-entry point is the sealing provider\n * (re-run {@link resolveManagedSecret} with the same provider to\n * re-resolve the passphrase, then {@link createOwnerKeyring}/load to\n * unlock — no interactive passphrase is ever required).\n *\n * There is deliberately no firm-controlled fallback: the marker's\n * `sealedUnder` is the single cryptographic anchor of ownership.\n */\nexport async function createDeedOwner(\n store: NoydbStore,\n vault: string,\n ownerUserId: string,\n sealing: SealingKeyProvider,\n): Promise<UnlockedKeyring> {\n // 1. Mint + seal + persist the random owner passphrase under the\n // non-firm provider (writes _meta/sealed-passphrase).\n const passphrase = await resolveManagedSecret(store, vault, sealing)\n\n // 2. Derive KEK_owner from the (never-human-seen) passphrase and\n // write the owner keyring file.\n const keyring = await createOwnerKeyring(store, vault, ownerUserId, passphrase)\n\n // 3. Write the plaintext Deed marker. NOT sealed — it is audit\n // metadata that must be readable without unlocking.\n await saveDeedMarker(store, vault, {\n ownerUserId,\n sealedUnder: sealing.id,\n latent: true,\n issuedAt: new Date().toISOString(),\n })\n\n return keyring\n}\n\n/**\n * Read the {@link DeedMarker} from `_meta/deed`, or `null` if the vault\n * has no Deed marker (i.e. it is not a Deed vault). Plaintext read — no\n * unlocking required.\n */\nexport async function loadDeedMarker(\n store: NoydbStore,\n vault: string,\n): Promise<DeedMarker | null> {\n const envelope = await store.get(vault, '_meta', DEED_RECORD_ID)\n if (!envelope) return null\n let payload: unknown\n try {\n payload = JSON.parse(envelope._data)\n } catch {\n return null\n }\n if (typeof payload !== 'object' || payload === null) return null\n const r = payload as Record<string, unknown>\n if (r._noydb_deed !== 1) return null\n if (\n typeof r.ownerUserId !== 'string'\n || typeof r.sealedUnder !== 'string'\n || r.latent !== true\n || typeof r.issuedAt !== 'string'\n ) {\n return null\n }\n const marker: DeedMarker = {\n ownerUserId: r.ownerUserId,\n sealedUnder: r.sealedUnder,\n latent: true,\n issuedAt: r.issuedAt,\n ...(typeof r.liberatedAt === 'string' ? { liberatedAt: r.liberatedAt } : {}),\n }\n return marker\n}\n\n/** Whether `vault` carries a Deed marker (a sealed/latent owner). */\nexport async function isDeedVault(\n store: NoydbStore,\n vault: string,\n): Promise<boolean> {\n return (await loadDeedMarker(store, vault)) !== null\n}\n\n/**\n * Persist a {@link DeedMarker} at `_meta/deed`. Mirrors\n * `saveSealedPassphrase`'s envelope shape: a standard\n * {@link EncryptedEnvelope} with AES-GCM bypassed (`_iv: ''`), the\n * payload stored as plaintext JSON in `_data`. The marker is metadata,\n * not a secret — sealing it would defeat its purpose (auditors /\n * custodians must read it without a key).\n *\n * @internal — used by `createDeedOwner` and (later) the Liberate\n * ceremony to stamp `liberatedAt`.\n */\nexport async function saveDeedMarker(\n store: NoydbStore,\n vault: string,\n marker: DeedMarker,\n): Promise<void> {\n const persisted: DeedEnvelopePayload = { _noydb_deed: 1, ...marker }\n const prior = await store.get(vault, '_meta', DEED_RECORD_ID)\n const env: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: (prior?._v ?? 0) + 1,\n _ts: new Date().toISOString(),\n // AES-GCM bypassed — the marker is plaintext audit metadata.\n _iv: '',\n _data: JSON.stringify(persisted),\n }\n await store.put(vault, '_meta', DEED_RECORD_ID, env)\n}\n","/**\n * `liberateVault` — the audited claim of ownership over a\n * sealed-owner (Deed) vault. The inverse of withdrawal.\n *\n * A **Deed** vault's owner credential is sealed under a non-firm provider, so\n * the firm-side **custodian** (which holds every collection DEK and operates\n * the vault fully) can never reach `KEK_owner`. Liberation is the ONLY route\n * by which a custodian assumes ownership, and it is deliberately a manual,\n * audited ceremony:\n *\n * 1. gate `'liberate-vault'` (fail-closed)\n * 2. caller MUST be the `custodian` (the de-facto authority holding the DEKs)\n * 3. freeze a PRE-liberation EVIDENCE snapshot (hash-pinned in the ledger) —\n * but PRESERVE the live data for the new owner (see the freeze decision\n * below)\n * 4. mint a NEW owner keyring re-wrapping the incumbent DEKs under the new\n * owner's KEK\n * 5. lifecycle ledger `liberation-claimed:<newOwnerId>:<legalBasis>`\n * 6. stamp the `_meta/deed` marker with `liberatedAt`\n *\n * ## Security: the inalienability floor\n *\n * Liberation **mints a new owner from the custodian's DEKs** — it does NOT\n * unseal the original sealed owner. The old sealed-owner credential is left\n * untouched and ORPHANED (its `_keyring/<id>` file remains, its KEK is still\n * sealed under the non-firm provider), never impersonated. The new owner is a\n * DISTINCT principal under a fresh KEK derived from `newOwnerPassphrase`. This\n * preserves the inalienability floor: the act of claiming ownership is itself\n * auditable and produces a different principal, rather than silently assuming\n * the latent owner's identity.\n *\n * ## Freeze decision: snapshot-only, not freeze-and-delete\n *\n * `freezeAndDeleteClosure` (withdraw-accessible.ts) writes a hash-pinned\n * snapshot and THEN delete-closures the live records — correct for a\n * destructive withdrawal, WRONG for liberation. Liberation transfers\n * operational continuity; it must leave the live data intact for the new\n * owner. We therefore call the snapshot-only core `freezeSnapshotOnly`\n * (factored out of that module; the freeze-AND-delete withdrawal path is\n * unchanged) to pin the evidence snapshot while preserving the records.\n *\n * @module\n */\n\nimport type { Vault } from '../../kernel/vault.js'\nimport type { FactorProofBundle, KeyringFile } from '../../kernel/types.js'\nimport { PermissionDeniedError } from '../../kernel/errors.js'\nimport { wrapKey } from '../../kernel/enclave/index.js'\nimport { createOwnerKeyring } from '../team/keyring.js'\nimport type { FrozenSnapshotRef } from '../../with-audit/portability/withdraw-accessible.js'\nimport { freezeSnapshotOnly } from '../../with-audit/portability/withdraw-accessible.js'\nimport { loadDeedMarker, saveDeedMarker } from '../team/deed.js'\n\nexport interface LiberateOptions {\n /** The id of the new owner principal the custodian mints by claiming ownership. */\n readonly newOwnerId: string\n /** The passphrase that derives the new owner's KEK (the DEKs are re-wrapped under it). */\n readonly newOwnerPassphrase: string\n /** Legal/contractual basis recorded in the audit (e.g. 'contractual-handover'). */\n readonly legalBasis: string\n readonly factors?: FactorProofBundle\n}\n\nexport interface LiberateResult {\n /** The hash-pinned pre-liberation evidence snapshot. */\n readonly snapshot: FrozenSnapshotRef\n}\n\n/**\n * Audited claim of ownership over a sealed-owner vault by its custodian. See\n * the module doc for the full ceremony + security rationale.\n */\nexport async function liberateVault(\n vault: Vault,\n opts: LiberateOptions,\n): Promise<LiberateResult> {\n // 1. gate — fail-closed `liberate-vault`.\n await vault.noydb.checkGate(vault.name, 'liberate-vault', opts.factors)\n\n // 2. caller must be the custodian (the de-facto authority holding the DEKs).\n const { name: vaultName, adapter, keyring } = vault._introspectState()\n if (keyring.role !== 'custodian') {\n throw new PermissionDeniedError(\n 'liberation is claimed only by the custodian (the de-facto authority holding the DEKs)',\n )\n }\n\n // Refuse to clobber an existing principal: the new owner must be a FRESH id.\n // (Checked before any side effect so a colliding id never leaves a half-run\n // ceremony — no orphan snapshot, no partial keyring overwrite.)\n const existing = await adapter.get(vaultName, '_keyring', opts.newOwnerId)\n if (existing) {\n throw new PermissionDeniedError(\n `liberateVault: newOwnerId \"${opts.newOwnerId}\" already exists as a principal; choose a fresh id (liberation mints a distinct owner, it never overwrites an existing keyring)`,\n )\n }\n\n // 3. Pre-liberation EVIDENCE snapshot of every operational collection —\n // snapshot-only (live data preserved for the new owner; liberation\n // transfers operational continuity, it does not erase).\n const collections = await listOperationalCollections(vault)\n const snapshot = await freezeSnapshotOnly(vault, collections, { actorUserId: keyring.userId })\n\n // 4. Mint a NEW owner keyring, re-wrapping the incumbent DEKs under the new\n // owner's fresh KEK. The old sealed owner is NOT unsealed — a DISTINCT\n // principal is minted and the original owner credential is left orphaned\n // (inalienability floor). Mirrors adopt-partition.ts's DEK re-wrap.\n const newOwner = await createOwnerKeyring(adapter, vaultName, opts.newOwnerId, opts.newOwnerPassphrase)\n if (!newOwner.kek) {\n throw new PermissionDeniedError(\n `new owner keyring for \"${opts.newOwnerId}\" has no KEK to re-wrap the incumbent DEKs under`,\n )\n }\n const env = await adapter.get(vaultName, '_keyring', opts.newOwnerId)\n if (!env) {\n throw new PermissionDeniedError(`new owner keyring for \"${opts.newOwnerId}\" did not persist`)\n }\n const keyringFile = JSON.parse(env._data) as KeyringFile\n const mergedDeks: Record<string, string> = { ...keyringFile.deks }\n for (const [collection, dek] of keyring.deks) {\n mergedDeks[collection] = await wrapKey(dek, newOwner.kek)\n }\n const mergedFile: KeyringFile = { ...keyringFile, deks: mergedDeks }\n await adapter.put(vaultName, '_keyring', opts.newOwnerId, { ...env, _data: JSON.stringify(mergedFile) })\n\n // 5. Lifecycle ledger audit (no-op if the history strategy is absent).\n await vault._getLedgerOrNull()?.append({\n op: 'lifecycle', collection: '', id: '', version: 0, actor: opts.newOwnerId, payloadHash: '',\n reason: `liberation-claimed:${opts.newOwnerId}:${opts.legalBasis}`,\n })\n\n // 6. Stamp the Deed marker with `liberatedAt` (if a marker exists).\n const marker = await loadDeedMarker(adapter, vaultName)\n if (marker) {\n await saveDeedMarker(adapter, vaultName, { ...marker, liberatedAt: new Date().toISOString() })\n }\n\n return { snapshot }\n}\n\n/**\n * Enumerate the vault's operational (user-facing) collections — those carried\n * by the caller's keyring DEK map, minus the reserved internal collections\n * (`_*`). These are the collections frozen into the evidence snapshot.\n */\nasync function listOperationalCollections(vault: Vault): Promise<readonly string[]> {\n const { keyring } = vault._introspectState()\n return [...keyring.deks.keys()].filter((c) => !c.startsWith('_'))\n}\n"],"mappings":"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","names":[]}