@noy-db/hub 0.6.0-pre.12 → 0.6.0-pre.13

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 (366) hide show
  1. package/dist/api-FYF5WE2E.js +20 -0
  2. package/dist/attestation/index.js +18 -18
  3. package/dist/{backup-AWHG6ODK.js → backup-JNCS4OMI.js} +15 -14
  4. package/dist/backup-JNCS4OMI.js.map +1 -0
  5. package/dist/blobs/index.js +13 -13
  6. package/dist/broker/index.js +12 -12
  7. package/dist/{bundle-handle-G4R37BQV.js → bundle-handle-RJZXOFXE.js} +2 -2
  8. package/dist/cargo/index.js +29 -29
  9. package/dist/{chunk-NJDQ4YSV.js → chunk-2BBXXSAD.js} +4 -4
  10. package/dist/{chunk-PK3URBVU.js → chunk-2KHT6EP3.js} +3 -3
  11. package/dist/{chunk-6HFM7EIT.js → chunk-2KX6MT3D.js} +2 -2
  12. package/dist/{chunk-AHLJSVRL.js → chunk-37M5QBAS.js} +2 -2
  13. package/dist/{chunk-TJCRCCPT.js → chunk-3EFQV2XQ.js} +4 -4
  14. package/dist/{chunk-747LRSOO.js → chunk-3IJK5FRG.js} +2 -2
  15. package/dist/{chunk-7MT56F4U.js → chunk-45UIDGPY.js} +3 -3
  16. package/dist/{chunk-XJIGOFN6.js → chunk-4R4N5PRM.js} +4 -4
  17. package/dist/{chunk-Z7SMBVNM.js → chunk-4TLFWYZG.js} +3 -3
  18. package/dist/{chunk-4HDYQL3J.js → chunk-4TRAQICY.js} +2 -2
  19. package/dist/{chunk-JIUVJ4M7.js → chunk-4WXISHDI.js} +2 -2
  20. package/dist/{chunk-SYFEJFIO.js → chunk-65NL6YZM.js} +8 -5
  21. package/dist/chunk-65NL6YZM.js.map +1 -0
  22. package/dist/{chunk-OF3L2SD6.js → chunk-6FXQZ3T7.js} +2 -2
  23. package/dist/{chunk-UHXCLPGC.js → chunk-7DK6CYXO.js} +2 -2
  24. package/dist/{chunk-NSRITINN.js → chunk-7GWC6RI5.js} +1 -1
  25. package/dist/chunk-7GWC6RI5.js.map +1 -0
  26. package/dist/{chunk-K525AZ5C.js → chunk-A5AB7EZU.js} +3 -3
  27. package/dist/{chunk-CEJWMBXO.js → chunk-A7BP42KY.js} +2 -2
  28. package/dist/{chunk-FDX7OWWI.js → chunk-ALBA4CET.js} +2 -2
  29. package/dist/{chunk-YAWK6AWF.js → chunk-ALDFGMO7.js} +2 -2
  30. package/dist/{chunk-ZSKB5BDK.js → chunk-AS53PYO6.js} +78 -78
  31. package/dist/{chunk-ZSKB5BDK.js.map → chunk-AS53PYO6.js.map} +1 -1
  32. package/dist/{chunk-3FSX6LDG.js → chunk-C5N3ZWGJ.js} +2 -2
  33. package/dist/{chunk-44JENXEG.js → chunk-C6RYZ4ZD.js} +2 -2
  34. package/dist/{chunk-7BEHYALK.js → chunk-CFM6GBNF.js} +7 -7
  35. package/dist/{chunk-7BEHYALK.js.map → chunk-CFM6GBNF.js.map} +1 -1
  36. package/dist/{chunk-VA3PPYTQ.js → chunk-CM7MGRP3.js} +6 -6
  37. package/dist/{chunk-HNGSV2YC.js → chunk-CT35TQV7.js} +1 -3
  38. package/dist/chunk-CT35TQV7.js.map +1 -0
  39. package/dist/{chunk-AR2JDFAE.js → chunk-CUEWN4FX.js} +2 -2
  40. package/dist/{chunk-DMQCLZ4Z.js → chunk-DKJXAZWF.js} +2 -2
  41. package/dist/{chunk-P2Y2F6EB.js → chunk-DMRDPZQ7.js} +4 -4
  42. package/dist/{chunk-G4VQSGXL.js → chunk-EPUDMBVL.js} +5 -5
  43. package/dist/{chunk-7G6VDWXB.js → chunk-EWTAUGAK.js} +3 -3
  44. package/dist/{chunk-4X5Z5SRV.js → chunk-EZYLQKHP.js} +35 -8
  45. package/dist/chunk-EZYLQKHP.js.map +1 -0
  46. package/dist/{chunk-35EBKWQU.js → chunk-FWZ7WYAH.js} +3 -3
  47. package/dist/{chunk-SPA5WX3T.js → chunk-GAELDPIH.js} +6 -12
  48. package/dist/chunk-GAELDPIH.js.map +1 -0
  49. package/dist/{chunk-7HF4J7Z5.js → chunk-GGM3JCEG.js} +2 -2
  50. package/dist/{chunk-OI7ZJUYT.js → chunk-GNMGX4FY.js} +8 -8
  51. package/dist/{chunk-NT432TE6.js → chunk-H3BDH7DA.js} +3 -3
  52. package/dist/{chunk-5GMMIDJX.js → chunk-H6UFT22R.js} +4 -4
  53. package/dist/{chunk-Z45YRGFO.js → chunk-HRQF23XS.js} +2 -2
  54. package/dist/{chunk-7F4DBNSP.js → chunk-IHKRTVJQ.js} +3 -3
  55. package/dist/{chunk-7TSYBD3E.js → chunk-IONRKUH4.js} +2 -2
  56. package/dist/{chunk-W3TVWF6N.js → chunk-IQVVUZ6Z.js} +2 -2
  57. package/dist/{chunk-GV2EFYNV.js → chunk-J5LO2H42.js} +4 -4
  58. package/dist/{chunk-WLYIROCJ.js → chunk-J6EBSGIR.js} +2 -2
  59. package/dist/{chunk-55LKVALY.js → chunk-JN64WH3U.js} +1 -3
  60. package/dist/chunk-JN64WH3U.js.map +1 -0
  61. package/dist/{chunk-RXYYFSBB.js → chunk-JTZRLD47.js} +3 -3
  62. package/dist/{chunk-B2P7M2D2.js → chunk-KMLMEIWV.js} +7 -11
  63. package/dist/chunk-KMLMEIWV.js.map +1 -0
  64. package/dist/{chunk-OXCU7G5I.js → chunk-KOM2GMTW.js} +10 -10
  65. package/dist/{chunk-UAHHWR4P.js → chunk-KYIXANXU.js} +7 -7
  66. package/dist/{chunk-E2TN45ZD.js → chunk-LOLK4EAT.js} +5 -5
  67. package/dist/{chunk-HVQGCKY4.js → chunk-LQEHLFFF.js} +2 -2
  68. package/dist/{chunk-H4YQSDOI.js → chunk-LQFNJRMI.js} +2 -2
  69. package/dist/{chunk-2APC6HS6.js → chunk-LVLXWIBF.js} +2 -2
  70. package/dist/{chunk-YY3PEDBR.js → chunk-LZHDIO64.js} +7 -7
  71. package/dist/{chunk-DG27IUVI.js → chunk-M3VRXAVD.js} +2 -2
  72. package/dist/{chunk-UFM3WVIF.js → chunk-M3XBZPLU.js} +6 -6
  73. package/dist/{chunk-PGWXE5CR.js → chunk-M44ZBLL3.js} +6 -6
  74. package/dist/{chunk-FNSMZQS4.js → chunk-MLMDO4OQ.js} +2 -2
  75. package/dist/{chunk-XXVIHJZY.js → chunk-MRDQ6HJS.js} +4 -4
  76. package/dist/{chunk-QS2PUZC7.js → chunk-MSCXCF4T.js} +6 -6
  77. package/dist/{chunk-UANGJ2YB.js → chunk-MUE2CQX2.js} +11 -10
  78. package/dist/{chunk-UANGJ2YB.js.map → chunk-MUE2CQX2.js.map} +1 -1
  79. package/dist/{chunk-VJFMXYKP.js → chunk-MVWO6KXU.js} +2 -2
  80. package/dist/{chunk-DEFCPAXD.js → chunk-NW3PSKAN.js} +2 -2
  81. package/dist/{chunk-CCZJJ4I2.js → chunk-OERLXUF5.js} +4 -4
  82. package/dist/{chunk-25FGCLYS.js → chunk-OFGLLNQW.js} +6 -6
  83. package/dist/{chunk-LRNODVQX.js → chunk-OGQSJJWF.js} +9 -9
  84. package/dist/{chunk-DIICZPWS.js → chunk-OOOU3F5Y.js} +5 -5
  85. package/dist/{chunk-HPLWFNG4.js → chunk-P7A2FX5Y.js} +2 -2
  86. package/dist/{chunk-KUDZVJKQ.js → chunk-PLJ622XY.js} +2 -2
  87. package/dist/{chunk-SV4BNALS.js → chunk-PRN65IDE.js} +5 -5
  88. package/dist/{chunk-VBCDVWRU.js → chunk-PV76W4KB.js} +7 -7
  89. package/dist/{chunk-RTAMLDWP.js → chunk-Q4AU3NJE.js} +2 -2
  90. package/dist/{chunk-T227WEHE.js → chunk-QLFLX5VA.js} +6 -6
  91. package/dist/{chunk-L2UHRVB4.js → chunk-QNGRTWGM.js} +5 -5
  92. package/dist/{chunk-P2PLXMHO.js → chunk-R6V4M2JO.js} +15 -12
  93. package/dist/chunk-R6V4M2JO.js.map +1 -0
  94. package/dist/{chunk-EBEKSNKS.js → chunk-S7UTUFPU.js} +11 -11
  95. package/dist/chunk-S7UTUFPU.js.map +1 -0
  96. package/dist/{chunk-RSHNUOSJ.js → chunk-SX2ZKKZS.js} +6 -6
  97. package/dist/{chunk-K6TQEDVJ.js → chunk-SX4HK6FU.js} +7 -7
  98. package/dist/{chunk-FUHKW252.js → chunk-TAFRRVFR.js} +2 -2
  99. package/dist/{chunk-JRCYZEHU.js → chunk-TEOL6VAV.js} +2 -2
  100. package/dist/{chunk-5XFTL73S.js → chunk-TJGB5I4J.js} +3 -3
  101. package/dist/{chunk-MSWYQLMQ.js → chunk-V3WLQXDF.js} +2 -2
  102. package/dist/{chunk-VMCCGUQB.js → chunk-VDQZKDED.js} +2 -2
  103. package/dist/{chunk-BGQ63MCT.js → chunk-VFBJMTLJ.js} +3 -3
  104. package/dist/{chunk-VCETL77O.js → chunk-WJD5VF2N.js} +14 -6
  105. package/dist/chunk-WJD5VF2N.js.map +1 -0
  106. package/dist/{chunk-N35NC4JH.js → chunk-WX5ABQSL.js} +2 -2
  107. package/dist/{chunk-FAXZQ5UF.js → chunk-Y2FDCDLY.js} +2 -2
  108. package/dist/{chunk-7VPWXVKU.js → chunk-Y6C67PJX.js} +5 -5
  109. package/dist/{chunk-Y3H4WOP3.js → chunk-YKDAFWWS.js} +5 -5
  110. package/dist/{chunk-HXGUIPQB.js → chunk-YRMA7CJH.js} +3 -3
  111. package/dist/{chunk-AZQVFXJF.js → chunk-YRTS5MWY.js} +3 -3
  112. package/dist/{chunk-HNHCQ556.js → chunk-YVGJLRXN.js} +2 -2
  113. package/dist/{chunk-6OC6YASM.js → chunk-YZC4DWJQ.js} +2 -2
  114. package/dist/{chunk-CCZ5DEJ7.js → chunk-Z7PW63ZC.js} +2 -2
  115. package/dist/{chunk-OUST5WDV.js → chunk-ZFO26ZZR.js} +5 -5
  116. package/dist/{chunk-4UVDI4XE.js → chunk-ZG7MXW5J.js} +3 -3
  117. package/dist/classified/index.js +5 -5
  118. package/dist/{classified-marker-EHRSGBM7.js → classified-marker-IYWA7L6U.js} +3 -3
  119. package/dist/collection-facade-FLXXGAPR.js +45 -0
  120. package/dist/{computed-6G5M3UPH.js → computed-B3BJQSU5.js} +3 -3
  121. package/dist/consent/index.js +9 -9
  122. package/dist/cover/index.js +3 -3
  123. package/dist/custody/index.js +11 -11
  124. package/dist/{dead-filter-ANPRM2LD.js → dead-filter-QV7ORQS3.js} +2 -2
  125. package/dist/debug/index.js +1 -1
  126. package/dist/{delegation-6NUQCURX.js → delegation-UPE5PUKB.js} +13 -11
  127. package/dist/delegation-UPE5PUKB.js.map +1 -0
  128. package/dist/derivations/index.js +12 -12
  129. package/dist/derive-R7ALNQRH.js +22 -0
  130. package/dist/directory/index.js +10 -10
  131. package/dist/{dispatch-EO2QHEXK.js → dispatch-BW2W2LXV.js} +18 -18
  132. package/dist/{dispatch-UGU775MV.js → dispatch-OBZS537P.js} +5 -5
  133. package/dist/{enclave-3Z7VTJAW.js → enclave-LXFVDJLO.js} +11 -9
  134. package/dist/executor-4WZG7D46.js +9 -0
  135. package/dist/executor-5RWPUTGR.js +29 -0
  136. package/dist/executor-VOD4C6ZA.js +9 -0
  137. package/dist/export-accessible-TCVU6B72.js +23 -0
  138. package/dist/extract-partition-UNUQF5MD.js +38 -0
  139. package/dist/{fanout-sidecar-3Q6BWNSS.js → fanout-sidecar-BD4ZSPER.js} +9 -9
  140. package/dist/find-QJPVK6CY.js +11 -0
  141. package/dist/forget/index.js +9 -9
  142. package/dist/guards/index.js +4 -4
  143. package/dist/history/index.js +12 -12
  144. package/dist/i18n/index.js +14 -14
  145. package/dist/index.d.ts +8 -8
  146. package/dist/index.js +91 -105
  147. package/dist/index.js.map +1 -1
  148. package/dist/indexing/index.js +2 -2
  149. package/dist/introspection/index.js +11 -11
  150. package/dist/issue-QYCCTU4S.js +19 -0
  151. package/dist/kernel/enclave/crypto.d.ts +24 -4
  152. package/dist/kernel/enclave/index.d.ts +2 -0
  153. package/dist/kernel/enclave/record-aad.d.ts +59 -0
  154. package/dist/kernel/errors.d.ts +0 -4
  155. package/dist/kernel/sync-policy.d.ts +0 -2
  156. package/dist/kernel/types.d.ts +2 -5
  157. package/dist/{ledger-ZO5OKKGR.js → ledger-6EMFGJRU.js} +11 -11
  158. package/dist/liberate-ZJDLMKOW.js +24 -0
  159. package/dist/{link-set-ZZYDZPL4.js → link-set-GUKFJQGW.js} +11 -11
  160. package/dist/{managed-secret-6FITSKDO.js → managed-secret-T6X4VHB2.js} +3 -3
  161. package/dist/materialized-views/index.js +20 -20
  162. package/dist/money/index.js +4 -4
  163. package/dist/noydb-IP2QPER4.js +83 -0
  164. package/dist/overlay-views/index.js +4 -4
  165. package/dist/periods/index.js +12 -12
  166. package/dist/pod/index.js +55 -67
  167. package/dist/policy/index.js +4 -4
  168. package/dist/port/to/index.d.ts +2 -2
  169. package/dist/port/with/service-bus.d.ts +0 -4
  170. package/dist/portability/index.js +8 -8
  171. package/dist/{post-register-577X4FAU.js → post-register-XKYJDDZL.js} +6 -6
  172. package/dist/query/index.js +3 -3
  173. package/dist/reduce/index.js +4 -4
  174. package/dist/register-GFYFTV74.js +32 -0
  175. package/dist/registry-BY7Z54RJ.js +9 -0
  176. package/dist/registry-D6NSZUVQ.js +9 -0
  177. package/dist/registry-KQPWMFIP.js +19 -0
  178. package/dist/registry-P7RU4TPR.js +18 -0
  179. package/dist/request-withdrawal-6RA6PGWD.js +31 -0
  180. package/dist/{reveal-PCQYK4NO.js → reveal-IN2ZBB5U.js} +6 -6
  181. package/dist/revoke-ETYVUNHE.js +24 -0
  182. package/dist/satellites/index.js +1 -1
  183. package/dist/schema-update/index.js +2 -2
  184. package/dist/sealed-record/index.js +12 -12
  185. package/dist/search/index.js +4 -4
  186. package/dist/{seed-BVO6SSKH.js → seed-D623RNG3.js} +11 -11
  187. package/dist/sequence/index.js +9 -9
  188. package/dist/session/index.js +9 -9
  189. package/dist/shadow/index.js +2 -2
  190. package/dist/signer-FLW4HHWM.js +25 -0
  191. package/dist/snapshots/index.js +10 -10
  192. package/dist/{stale-3FW6U4OU.js → stale-O3CZVYMO.js} +10 -10
  193. package/dist/storage-7GVHLKVJ.js +23 -0
  194. package/dist/{store-coordination-provider-3QGSXG3P.js → store-coordination-provider-RKBDHHN4.js} +3 -3
  195. package/dist/sync/index.js +11 -11
  196. package/dist/team/index.js +14 -14
  197. package/dist/tiers/index.js +10 -10
  198. package/dist/to/index.js +1 -3
  199. package/dist/to/index.js.map +1 -1
  200. package/dist/transactions/index.js +3 -3
  201. package/dist/util/index.js +1 -1
  202. package/dist/{verify-RO5RTZOJ.js → verify-GB7SBYC3.js} +5 -5
  203. package/dist/walk-77FSILOU.js +21 -0
  204. package/dist/with-pod/bundle.d.ts +3 -11
  205. package/dist/with-pod/format.d.ts +0 -2
  206. package/dist/with-pod/index.d.ts +6 -6
  207. package/dist/with-pod/pod-store.d.ts +0 -8
  208. package/dist/withdraw-accessible-XMWJWDZP.js +28 -0
  209. package/package.json +3 -3
  210. package/dist/api-XDMGLURJ.js +0 -20
  211. package/dist/backup-AWHG6ODK.js.map +0 -1
  212. package/dist/chunk-4X5Z5SRV.js.map +0 -1
  213. package/dist/chunk-55LKVALY.js.map +0 -1
  214. package/dist/chunk-B2P7M2D2.js.map +0 -1
  215. package/dist/chunk-EBEKSNKS.js.map +0 -1
  216. package/dist/chunk-HNGSV2YC.js.map +0 -1
  217. package/dist/chunk-NSRITINN.js.map +0 -1
  218. package/dist/chunk-P2PLXMHO.js.map +0 -1
  219. package/dist/chunk-SPA5WX3T.js.map +0 -1
  220. package/dist/chunk-SYFEJFIO.js.map +0 -1
  221. package/dist/chunk-VCETL77O.js.map +0 -1
  222. package/dist/collection-facade-5UJW6QRB.js +0 -45
  223. package/dist/delegation-6NUQCURX.js.map +0 -1
  224. package/dist/derive-5PFJNNNU.js +0 -22
  225. package/dist/executor-AB4BCJO4.js +0 -29
  226. package/dist/executor-CYJQLKKI.js +0 -9
  227. package/dist/executor-S4QIKEHW.js +0 -9
  228. package/dist/export-accessible-JXCJGHCF.js +0 -23
  229. package/dist/extract-partition-2WDL4FJ5.js +0 -38
  230. package/dist/find-RBBYKXI7.js +0 -11
  231. package/dist/issue-EABYJWX5.js +0 -19
  232. package/dist/liberate-S3JR4TQT.js +0 -24
  233. package/dist/noydb-SPEVD5JA.js +0 -83
  234. package/dist/register-OXUXXIWT.js +0 -32
  235. package/dist/registry-CZVINVQH.js +0 -9
  236. package/dist/registry-IKUR2MXD.js +0 -9
  237. package/dist/registry-L3CO5ZAY.js +0 -18
  238. package/dist/registry-QFIVGS2W.js +0 -19
  239. package/dist/request-withdrawal-UV42CNR5.js +0 -31
  240. package/dist/revoke-26DFO36Z.js +0 -24
  241. package/dist/signer-DDO7QEID.js +0 -25
  242. package/dist/storage-M7L2EK3F.js +0 -23
  243. package/dist/walk-OFXQN5US.js +0 -21
  244. package/dist/withdraw-accessible-7XKYVTMP.js +0 -28
  245. /package/dist/{api-XDMGLURJ.js.map → api-FYF5WE2E.js.map} +0 -0
  246. /package/dist/{bundle-handle-G4R37BQV.js.map → bundle-handle-RJZXOFXE.js.map} +0 -0
  247. /package/dist/{chunk-NJDQ4YSV.js.map → chunk-2BBXXSAD.js.map} +0 -0
  248. /package/dist/{chunk-PK3URBVU.js.map → chunk-2KHT6EP3.js.map} +0 -0
  249. /package/dist/{chunk-6HFM7EIT.js.map → chunk-2KX6MT3D.js.map} +0 -0
  250. /package/dist/{chunk-AHLJSVRL.js.map → chunk-37M5QBAS.js.map} +0 -0
  251. /package/dist/{chunk-TJCRCCPT.js.map → chunk-3EFQV2XQ.js.map} +0 -0
  252. /package/dist/{chunk-747LRSOO.js.map → chunk-3IJK5FRG.js.map} +0 -0
  253. /package/dist/{chunk-7MT56F4U.js.map → chunk-45UIDGPY.js.map} +0 -0
  254. /package/dist/{chunk-XJIGOFN6.js.map → chunk-4R4N5PRM.js.map} +0 -0
  255. /package/dist/{chunk-Z7SMBVNM.js.map → chunk-4TLFWYZG.js.map} +0 -0
  256. /package/dist/{chunk-4HDYQL3J.js.map → chunk-4TRAQICY.js.map} +0 -0
  257. /package/dist/{chunk-JIUVJ4M7.js.map → chunk-4WXISHDI.js.map} +0 -0
  258. /package/dist/{chunk-OF3L2SD6.js.map → chunk-6FXQZ3T7.js.map} +0 -0
  259. /package/dist/{chunk-UHXCLPGC.js.map → chunk-7DK6CYXO.js.map} +0 -0
  260. /package/dist/{chunk-K525AZ5C.js.map → chunk-A5AB7EZU.js.map} +0 -0
  261. /package/dist/{chunk-CEJWMBXO.js.map → chunk-A7BP42KY.js.map} +0 -0
  262. /package/dist/{chunk-FDX7OWWI.js.map → chunk-ALBA4CET.js.map} +0 -0
  263. /package/dist/{chunk-YAWK6AWF.js.map → chunk-ALDFGMO7.js.map} +0 -0
  264. /package/dist/{chunk-3FSX6LDG.js.map → chunk-C5N3ZWGJ.js.map} +0 -0
  265. /package/dist/{chunk-44JENXEG.js.map → chunk-C6RYZ4ZD.js.map} +0 -0
  266. /package/dist/{chunk-VA3PPYTQ.js.map → chunk-CM7MGRP3.js.map} +0 -0
  267. /package/dist/{chunk-AR2JDFAE.js.map → chunk-CUEWN4FX.js.map} +0 -0
  268. /package/dist/{chunk-DMQCLZ4Z.js.map → chunk-DKJXAZWF.js.map} +0 -0
  269. /package/dist/{chunk-P2Y2F6EB.js.map → chunk-DMRDPZQ7.js.map} +0 -0
  270. /package/dist/{chunk-G4VQSGXL.js.map → chunk-EPUDMBVL.js.map} +0 -0
  271. /package/dist/{chunk-7G6VDWXB.js.map → chunk-EWTAUGAK.js.map} +0 -0
  272. /package/dist/{chunk-35EBKWQU.js.map → chunk-FWZ7WYAH.js.map} +0 -0
  273. /package/dist/{chunk-7HF4J7Z5.js.map → chunk-GGM3JCEG.js.map} +0 -0
  274. /package/dist/{chunk-OI7ZJUYT.js.map → chunk-GNMGX4FY.js.map} +0 -0
  275. /package/dist/{chunk-NT432TE6.js.map → chunk-H3BDH7DA.js.map} +0 -0
  276. /package/dist/{chunk-5GMMIDJX.js.map → chunk-H6UFT22R.js.map} +0 -0
  277. /package/dist/{chunk-Z45YRGFO.js.map → chunk-HRQF23XS.js.map} +0 -0
  278. /package/dist/{chunk-7F4DBNSP.js.map → chunk-IHKRTVJQ.js.map} +0 -0
  279. /package/dist/{chunk-7TSYBD3E.js.map → chunk-IONRKUH4.js.map} +0 -0
  280. /package/dist/{chunk-W3TVWF6N.js.map → chunk-IQVVUZ6Z.js.map} +0 -0
  281. /package/dist/{chunk-GV2EFYNV.js.map → chunk-J5LO2H42.js.map} +0 -0
  282. /package/dist/{chunk-WLYIROCJ.js.map → chunk-J6EBSGIR.js.map} +0 -0
  283. /package/dist/{chunk-RXYYFSBB.js.map → chunk-JTZRLD47.js.map} +0 -0
  284. /package/dist/{chunk-OXCU7G5I.js.map → chunk-KOM2GMTW.js.map} +0 -0
  285. /package/dist/{chunk-UAHHWR4P.js.map → chunk-KYIXANXU.js.map} +0 -0
  286. /package/dist/{chunk-E2TN45ZD.js.map → chunk-LOLK4EAT.js.map} +0 -0
  287. /package/dist/{chunk-HVQGCKY4.js.map → chunk-LQEHLFFF.js.map} +0 -0
  288. /package/dist/{chunk-H4YQSDOI.js.map → chunk-LQFNJRMI.js.map} +0 -0
  289. /package/dist/{chunk-2APC6HS6.js.map → chunk-LVLXWIBF.js.map} +0 -0
  290. /package/dist/{chunk-YY3PEDBR.js.map → chunk-LZHDIO64.js.map} +0 -0
  291. /package/dist/{chunk-DG27IUVI.js.map → chunk-M3VRXAVD.js.map} +0 -0
  292. /package/dist/{chunk-UFM3WVIF.js.map → chunk-M3XBZPLU.js.map} +0 -0
  293. /package/dist/{chunk-PGWXE5CR.js.map → chunk-M44ZBLL3.js.map} +0 -0
  294. /package/dist/{chunk-FNSMZQS4.js.map → chunk-MLMDO4OQ.js.map} +0 -0
  295. /package/dist/{chunk-XXVIHJZY.js.map → chunk-MRDQ6HJS.js.map} +0 -0
  296. /package/dist/{chunk-QS2PUZC7.js.map → chunk-MSCXCF4T.js.map} +0 -0
  297. /package/dist/{chunk-VJFMXYKP.js.map → chunk-MVWO6KXU.js.map} +0 -0
  298. /package/dist/{chunk-DEFCPAXD.js.map → chunk-NW3PSKAN.js.map} +0 -0
  299. /package/dist/{chunk-CCZJJ4I2.js.map → chunk-OERLXUF5.js.map} +0 -0
  300. /package/dist/{chunk-25FGCLYS.js.map → chunk-OFGLLNQW.js.map} +0 -0
  301. /package/dist/{chunk-LRNODVQX.js.map → chunk-OGQSJJWF.js.map} +0 -0
  302. /package/dist/{chunk-DIICZPWS.js.map → chunk-OOOU3F5Y.js.map} +0 -0
  303. /package/dist/{chunk-HPLWFNG4.js.map → chunk-P7A2FX5Y.js.map} +0 -0
  304. /package/dist/{chunk-KUDZVJKQ.js.map → chunk-PLJ622XY.js.map} +0 -0
  305. /package/dist/{chunk-SV4BNALS.js.map → chunk-PRN65IDE.js.map} +0 -0
  306. /package/dist/{chunk-VBCDVWRU.js.map → chunk-PV76W4KB.js.map} +0 -0
  307. /package/dist/{chunk-RTAMLDWP.js.map → chunk-Q4AU3NJE.js.map} +0 -0
  308. /package/dist/{chunk-T227WEHE.js.map → chunk-QLFLX5VA.js.map} +0 -0
  309. /package/dist/{chunk-L2UHRVB4.js.map → chunk-QNGRTWGM.js.map} +0 -0
  310. /package/dist/{chunk-RSHNUOSJ.js.map → chunk-SX2ZKKZS.js.map} +0 -0
  311. /package/dist/{chunk-K6TQEDVJ.js.map → chunk-SX4HK6FU.js.map} +0 -0
  312. /package/dist/{chunk-FUHKW252.js.map → chunk-TAFRRVFR.js.map} +0 -0
  313. /package/dist/{chunk-JRCYZEHU.js.map → chunk-TEOL6VAV.js.map} +0 -0
  314. /package/dist/{chunk-5XFTL73S.js.map → chunk-TJGB5I4J.js.map} +0 -0
  315. /package/dist/{chunk-MSWYQLMQ.js.map → chunk-V3WLQXDF.js.map} +0 -0
  316. /package/dist/{chunk-VMCCGUQB.js.map → chunk-VDQZKDED.js.map} +0 -0
  317. /package/dist/{chunk-BGQ63MCT.js.map → chunk-VFBJMTLJ.js.map} +0 -0
  318. /package/dist/{chunk-N35NC4JH.js.map → chunk-WX5ABQSL.js.map} +0 -0
  319. /package/dist/{chunk-FAXZQ5UF.js.map → chunk-Y2FDCDLY.js.map} +0 -0
  320. /package/dist/{chunk-7VPWXVKU.js.map → chunk-Y6C67PJX.js.map} +0 -0
  321. /package/dist/{chunk-Y3H4WOP3.js.map → chunk-YKDAFWWS.js.map} +0 -0
  322. /package/dist/{chunk-HXGUIPQB.js.map → chunk-YRMA7CJH.js.map} +0 -0
  323. /package/dist/{chunk-AZQVFXJF.js.map → chunk-YRTS5MWY.js.map} +0 -0
  324. /package/dist/{chunk-HNHCQ556.js.map → chunk-YVGJLRXN.js.map} +0 -0
  325. /package/dist/{chunk-6OC6YASM.js.map → chunk-YZC4DWJQ.js.map} +0 -0
  326. /package/dist/{chunk-CCZ5DEJ7.js.map → chunk-Z7PW63ZC.js.map} +0 -0
  327. /package/dist/{chunk-OUST5WDV.js.map → chunk-ZFO26ZZR.js.map} +0 -0
  328. /package/dist/{chunk-4UVDI4XE.js.map → chunk-ZG7MXW5J.js.map} +0 -0
  329. /package/dist/{classified-marker-EHRSGBM7.js.map → classified-marker-IYWA7L6U.js.map} +0 -0
  330. /package/dist/{collection-facade-5UJW6QRB.js.map → collection-facade-FLXXGAPR.js.map} +0 -0
  331. /package/dist/{computed-6G5M3UPH.js.map → computed-B3BJQSU5.js.map} +0 -0
  332. /package/dist/{dead-filter-ANPRM2LD.js.map → dead-filter-QV7ORQS3.js.map} +0 -0
  333. /package/dist/{derive-5PFJNNNU.js.map → derive-R7ALNQRH.js.map} +0 -0
  334. /package/dist/{dispatch-EO2QHEXK.js.map → dispatch-BW2W2LXV.js.map} +0 -0
  335. /package/dist/{dispatch-UGU775MV.js.map → dispatch-OBZS537P.js.map} +0 -0
  336. /package/dist/{enclave-3Z7VTJAW.js.map → enclave-LXFVDJLO.js.map} +0 -0
  337. /package/dist/{executor-AB4BCJO4.js.map → executor-4WZG7D46.js.map} +0 -0
  338. /package/dist/{executor-CYJQLKKI.js.map → executor-5RWPUTGR.js.map} +0 -0
  339. /package/dist/{executor-S4QIKEHW.js.map → executor-VOD4C6ZA.js.map} +0 -0
  340. /package/dist/{export-accessible-JXCJGHCF.js.map → export-accessible-TCVU6B72.js.map} +0 -0
  341. /package/dist/{extract-partition-2WDL4FJ5.js.map → extract-partition-UNUQF5MD.js.map} +0 -0
  342. /package/dist/{fanout-sidecar-3Q6BWNSS.js.map → fanout-sidecar-BD4ZSPER.js.map} +0 -0
  343. /package/dist/{find-RBBYKXI7.js.map → find-QJPVK6CY.js.map} +0 -0
  344. /package/dist/{issue-EABYJWX5.js.map → issue-QYCCTU4S.js.map} +0 -0
  345. /package/dist/{ledger-ZO5OKKGR.js.map → ledger-6EMFGJRU.js.map} +0 -0
  346. /package/dist/{liberate-S3JR4TQT.js.map → liberate-ZJDLMKOW.js.map} +0 -0
  347. /package/dist/{link-set-ZZYDZPL4.js.map → link-set-GUKFJQGW.js.map} +0 -0
  348. /package/dist/{managed-secret-6FITSKDO.js.map → managed-secret-T6X4VHB2.js.map} +0 -0
  349. /package/dist/{noydb-SPEVD5JA.js.map → noydb-IP2QPER4.js.map} +0 -0
  350. /package/dist/{post-register-577X4FAU.js.map → post-register-XKYJDDZL.js.map} +0 -0
  351. /package/dist/{register-OXUXXIWT.js.map → register-GFYFTV74.js.map} +0 -0
  352. /package/dist/{registry-CZVINVQH.js.map → registry-BY7Z54RJ.js.map} +0 -0
  353. /package/dist/{registry-IKUR2MXD.js.map → registry-D6NSZUVQ.js.map} +0 -0
  354. /package/dist/{registry-L3CO5ZAY.js.map → registry-KQPWMFIP.js.map} +0 -0
  355. /package/dist/{registry-QFIVGS2W.js.map → registry-P7RU4TPR.js.map} +0 -0
  356. /package/dist/{request-withdrawal-UV42CNR5.js.map → request-withdrawal-6RA6PGWD.js.map} +0 -0
  357. /package/dist/{reveal-PCQYK4NO.js.map → reveal-IN2ZBB5U.js.map} +0 -0
  358. /package/dist/{revoke-26DFO36Z.js.map → revoke-ETYVUNHE.js.map} +0 -0
  359. /package/dist/{seed-BVO6SSKH.js.map → seed-D623RNG3.js.map} +0 -0
  360. /package/dist/{signer-DDO7QEID.js.map → signer-FLW4HHWM.js.map} +0 -0
  361. /package/dist/{stale-3FW6U4OU.js.map → stale-O3CZVYMO.js.map} +0 -0
  362. /package/dist/{storage-M7L2EK3F.js.map → storage-7GVHLKVJ.js.map} +0 -0
  363. /package/dist/{store-coordination-provider-3QGSXG3P.js.map → store-coordination-provider-RKBDHHN4.js.map} +0 -0
  364. /package/dist/{verify-RO5RTZOJ.js.map → verify-GB7SBYC3.js.map} +0 -0
  365. /package/dist/{walk-OFXQN5US.js.map → walk-77FSILOU.js.map} +0 -0
  366. /package/dist/{withdraw-accessible-7XKYVTMP.js.map → withdraw-accessible-XMWJWDZP.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/kernel/enclave/record-keys/lifecycle.ts","../src/kernel/enclave/record-keys/sealed-slots.ts","../src/kernel/enclave/classify/write.ts","../src/kernel/schema.ts","../src/kernel/enclave/record-keys/record-codec.ts","../src/kernel/enclave/record-keys/sealing.ts","../src/kernel/enclave/record-keys/deterministic.ts","../src/kernel/enclave/record-keys/envelope-body.ts","../src/kernel/enclave/broker/proof.ts"],"sourcesContent":["/**\n * Per-record CEK lifecycle helpers for the WRITE path.\n *\n * These are the collection-coupled CEK operations, lifted off `Collection`\n * behind narrow deps so the kernel file delegates instead of carrying the\n * crypto inline:\n *\n * - {@link resolveStableCek} — the stable-CEK rule: an update or history\n * snapshot reuses the record's existing CEK so a single CEK delete kills\n * the whole version chain; a genuine insert (or legacy record being\n * migrated) mints a fresh one.\n * - {@link rewrapBodyToDek} — move a record body's wrapping from one DEK to\n * another (tier elevate/demote, bundle re-key) WITHOUT changing the body\n * key: unwrap the CEK under the source DEK, re-encrypt the body under the\n * same CEK, re-wrap the CEK under the destination DEK. History-chain\n * identity is preserved because the body key never changes.\n *\n * Both are pure functions over their deps — the `cekCache` ownership stays on\n * the collection (its lifetime is tied to `load()`), so callers cache the\n * returned key themselves.\n */\nimport { encrypt, decrypt, generateDEK, wrapCek, unwrapCek, type EnclaveKey } from '../crypto.js'\nimport type { EncryptedEnvelope } from '../../types.js'\nimport type { Lru } from '../../cache/index.js'\n\n/** Dependencies {@link resolveStableCek} needs from its collection. */\nexport interface StableCekDeps {\n /** The collection's per-record CEK cache (`null` → no caching). */\n readonly cache: Lru<string, EnclaveKey> | null\n /** Read the record's live envelope (to recover an existing `_cek`). */\n getLive(id: string): Promise<EncryptedEnvelope | null>\n /** The DEK the CEK is wrapped under (the collection DEK on the normal path). */\n getDEK(): Promise<EnclaveKey>\n}\n\n/**\n * Resolve the stable CEK for a record on the write path. Caches the resolved\n * key under `id` so an update + its history snapshot share one CEK.\n */\nexport async function resolveStableCek(deps: StableCekDeps, id: string): Promise<EnclaveKey> {\n const cached = deps.cache?.get(id)\n if (cached) return cached\n\n const live = await deps.getLive(id)\n if (live?._cek !== undefined) {\n const cek = await unwrapCek(live._cek, await deps.getDEK())\n deps.cache?.set(id, cek, 1)\n return cek\n }\n\n const fresh = await generateDEK()\n deps.cache?.set(id, fresh, 1)\n return fresh\n}\n\n/** Re-wrapped body bytes + the (optional) CEK to cache. */\nexport interface RewrappedBody {\n readonly _iv: string\n readonly _data: string\n /** Present iff the source envelope carried a CEK (per-record-key record). */\n readonly _cek?: string\n /** The body key when one exists, so the caller can cache it; `null` for a legacy record. */\n readonly cek: EnclaveKey | null\n}\n\n/**\n * Move a record body from `fromDek` to `toDek`.\n *\n * - Per-record-key record (`_cek` present): unwrap the CEK under `fromDek`,\n * re-encrypt the body under the SAME CEK, re-wrap the CEK under `toDek`. The\n * body key is unchanged → history-chain identity preserved.\n * - Legacy record (no `_cek`): decrypt under `fromDek`, re-encrypt under `toDek`\n * directly — byte-for-byte the pre-CEK behaviour.\n */\nexport async function rewrapBodyToDek(\n envelope: Pick<EncryptedEnvelope, '_iv' | '_data' | '_cek'>,\n fromDek: EnclaveKey,\n toDek: EnclaveKey,\n): Promise<RewrappedBody> {\n if (envelope._cek !== undefined) {\n const cek = await unwrapCek(envelope._cek, fromDek)\n const plaintext = await decrypt(envelope._iv, envelope._data, cek)\n const { iv, data } = await encrypt(plaintext, cek)\n return { _iv: iv, _data: data, _cek: await wrapCek(cek, toDek), cek }\n }\n const plaintext = await decrypt(envelope._iv, envelope._data, fromDek)\n const { iv, data } = await encrypt(plaintext, toDek)\n return { _iv: iv, _data: data, cek: null }\n}\n\n/**\n * Merge an ALREADY-rewrapped {@link RewrappedBody} into `envelope`, keeping\n * every field but the re-keyed body (`_iv`/`_data`/`_cek`) unchanged.\n *\n * Body-field access (`_iv`/`_data`/`_cek`) is sanctioned inside the enclave;\n * this lets callers outside it get back a ready-to-store envelope without\n * reading those fields themselves. Takes a body the caller has ALREADY\n * computed (vs. {@link rewrapEnvelope}, which also runs `rewrapBodyToDek`)\n * so a caller building a SECOND envelope from the same rewrap — a tier\n * move's live write AND its pre-move `_history` snapshot, #728 — pays the\n * unwrap/decrypt/encrypt/wrap crypto once, not twice.\n */\nexport function applyRewrappedBody(envelope: EncryptedEnvelope, body: RewrappedBody): EncryptedEnvelope {\n const next: EncryptedEnvelope = {\n ...envelope,\n _iv: body._iv,\n _data: body._data,\n ...(body._cek !== undefined ? { _cek: body._cek } : {}),\n }\n if (body._cek === undefined && envelope._cek !== undefined) {\n delete (next as { _cek?: string })._cek\n }\n return next\n}\n\n/**\n * Move a FULL stored envelope from `fromDek` to `toDek`, preserving every\n * field other than the re-keyed body (`_iv`/`_data`/`_cek`) unchanged.\n * Mirrors the spread-merge idiom `with-audit/tiers/index.ts`'s\n * `elevate()`/`demote()` use to apply `rewrapBodyToDek`'s result to the live\n * envelope, generalized to any envelope shape (history callers don't bump\n * `_v`/`_ts`/`_tier` the way a tier move does).\n */\nexport async function rewrapEnvelope(\n envelope: EncryptedEnvelope,\n fromDek: EnclaveKey,\n toDek: EnclaveKey,\n): Promise<EncryptedEnvelope> {\n return applyRewrappedBody(envelope, await rewrapBodyToDek(envelope, fromDek, toDek))\n}\n\n/**\n * Is `envelope` ALREADY wrapped under `dek` (a per-record CEK wrapped under\n * `dek`, or — legacy, no `_cek` — the body itself decryptable under `dek`)?\n *\n * A toDek-first idempotency check for retry-safe rewrap loops (#712 crash\n * atomicity, `with-commit/history/history.ts`'s `rewrapHistory`): a crash\n * mid-rewrap can strand some entries already moved to `toDek` while others\n * remain under `fromDek`. Calling this with `toDek` before attempting the\n * `fromDek` rewrap lets the caller skip an already-migrated entry instead of\n * re-wrapping it (a re-wrap would be a correctness no-op but doubles as an\n * unnecessary write) — and, more importantly, tells a retry loop that this\n * entry needs no `fromDek` unwrap attempt at all, so a crash that landed some\n * entries under `toDek` doesn't wedge on those already-moved entries.\n *\n * AES-KW (CEK unwrap) and AES-GCM (direct decrypt) are both authenticated —\n * unwrap/decrypt under the wrong key throws, never silently returns garbage\n * — so a caught error here reliably means \"not wrapped under `dek`\", not a\n * corrupt envelope (a genuinely corrupt envelope also throws under the\n * correct key, but callers that reach this helper only do so as a\n * best-effort probe before their own decisive fromDek attempt, which still\n * surfaces real corruption).\n *\n * Body-field access (`_iv`/`_data`/`_cek`) is sanctioned inside the enclave —\n * this is the sanctioned door so callers outside it (history.ts) never read\n * those fields themselves, preserving the check-architecture body-access\n * ratchet.\n */\nexport async function isRewrappedUnder(\n envelope: Pick<EncryptedEnvelope, '_iv' | '_data' | '_cek'>,\n dek: EnclaveKey,\n): Promise<boolean> {\n try {\n if (envelope._cek !== undefined) {\n await unwrapCek(envelope._cek, dek)\n } else {\n await decrypt(envelope._iv, envelope._data, dek)\n }\n return true\n } catch {\n return false\n }\n}\n","/**\n * Sealed-slot sub-step — the \"seal these declared fields into their own\n * `iv:data` slots\" mechanism, extracted byte-parity from `RecordCodec`\n * (#629 Task 1; see `.superpowers/sdd/seam-map-classified-blobs.md` §2).\n *\n * This is the ONE genuinely separable sub-step `RecordCodec.encryptRecord`/\n * `decryptRecord` perform (the digest-only `_vdig`/`_bidx` mechanism is NOT\n * separable the same way — it needs write-history `prev` context, see the\n * seam map's separability verdict). `sealFields`/`unsealFields` are the\n * natural shape of a `via` `encodeAtRest`/`decodeAtRest` hook for a \"seal\n * this field\" via-feature, and `makeSealedSlotCapability`/\n * `makeReservedEnvelopes` (below) are the `ViaCryptoCtx` capability\n * factories built on top of them.\n *\n * The capability factories live here (rather than a sibling\n * `kernel/via-crypto.ts`) so they can call `deriveSealedFieldKey`/\n * `deriveSealedFieldKeyFromCek`/`encrypt`/`decrypt` directly — this file IS\n * kernel enclave code (C3), so it is exempt from `enclave-barrel-only` the\n * same way `record-codec.ts`/`sealing.ts` are. A file outside\n * `kernel/enclave/**` would have had to go through the enclave barrel\n * (`kernel/enclave/index.ts`), which does not (yet) export these symbols.\n */\nimport { encrypt, decrypt, deriveSealedFieldKey, deriveSealedFieldKeyFromCek, type EnclaveKey } from '../crypto.js'\nimport { dualReadSealedSlot } from './sealed-slot.js'\nimport { NOYDB_FORMAT_VERSION, SealedHandle, type EncryptedEnvelope } from '../../types.js'\nimport { ValidationError } from '../../errors.js'\nimport type { SealedSlotRef, ViaCryptoCtx } from '../../via/index.js'\n\n/**\n * Key material for one collection's sealed-field derivation. `cek`, when\n * supplied, is preferred (record-scoped); `getDEK` is the fallback (and,\n * for legacy/no-CEK collections, the only key) — mirrors the dual-read\n * `RecordCodec` has always done.\n */\nexport interface SealKeyMaterial {\n readonly collection: string\n readonly cek?: EnclaveKey\n getDEK(): Promise<EnclaveKey>\n}\n\nexport interface SealFieldsResult {\n readonly openRecord: Record<string, unknown>\n readonly sealed: Record<string, string> | undefined\n}\n\n/** Seal one field's value to an `iv:data` slot under its derived per-field key. */\nasync function sealOneFieldWithDek(\n field: string,\n value: unknown,\n dek: EnclaveKey,\n keyMaterial: SealKeyMaterial,\n): Promise<SealedSlotRef> {\n const fieldKey = keyMaterial.cek !== undefined\n ? await deriveSealedFieldKeyFromCek(keyMaterial.cek, keyMaterial.collection, field)\n : await deriveSealedFieldKey(dek, keyMaterial.collection, field)\n return encrypt(JSON.stringify(value), fieldKey)\n}\n\n/**\n * Seal one field's value on demand — fetches the DEK itself. A singular\n * counterpart to {@link sealFields} for callers that seal one field at a\n * time rather than a whole record. Not exported — `makeSealedSlotCapability`\n * is the only caller (#629 Task 11: knip-flagged unused export, dropped;\n * intra-module use only).\n */\nasync function sealOneField(\n field: string,\n value: unknown,\n keyMaterial: SealKeyMaterial,\n): Promise<SealedSlotRef> {\n const dek = await keyMaterial.getDEK()\n return sealOneFieldWithDek(field, value, dek, keyMaterial)\n}\n\n/**\n * Peel declared `sensitiveFields` out of `record` BEFORE building `_data`,\n * sealing each into its own `iv:data` slot. Extracted byte-parity from\n * `RecordCodec.encryptRecord` step 2 (pre-#629 `record-codec.ts:261-288`).\n * Returns `record` untouched (and `sealed: undefined`) when no declared\n * field is present with a defined value — the envelope stays byte-identical\n * to legacy output, exactly as before.\n */\nexport async function sealFields(\n record: Record<string, unknown>,\n sensitiveFields: ReadonlySet<string>,\n keyMaterial: SealKeyMaterial,\n): Promise<SealFieldsResult> {\n const dek = await keyMaterial.getDEK()\n const open: Record<string, unknown> = { ...record }\n const slots: Record<string, string> = {}\n for (const field of sensitiveFields) {\n if (!(field in record)) continue\n const value = record[field]\n if (value === undefined) continue\n const { iv, data } = await sealOneFieldWithDek(field, value, dek, keyMaterial)\n slots[field] = `${iv}:${data}`\n delete open[field]\n }\n if (Object.keys(slots).length === 0) return { openRecord: record, sealed: undefined }\n return { openRecord: open, sealed: slots }\n}\n\n/**\n * Unseal one `_sealed[field]` slot to its plaintext value: dual-read (try\n * the CEK-derived key, fall back to the DEK-derived key for legacy\n * records), AES-GCM-decrypt the `iv:data` blob, and JSON-parse the result.\n * Extracted byte-parity from `RecordCodec.unsealField`\n * (pre-#629 `record-codec.ts:485-493`).\n */\nexport async function unsealOneField(field: string, blob: string, keyMaterial: SealKeyMaterial): Promise<unknown> {\n const dek = await keyMaterial.getDEK()\n return JSON.parse(await dualReadSealedSlot(blob, field, keyMaterial.collection, keyMaterial.cek, dek))\n}\n\n/**\n * Build a non-leaking {@link SealedHandle} producer over sealed key\n * material: each call captures only the ciphertext `blob` and a closure to\n * {@link unsealOneField} — the plaintext is never stored on the handle, and\n * (crucially) the DEK is fetched lazily on `reveal()`, never at handle-\n * construction time. Extracted byte-parity from\n * `RecordCodec.makeSealedHandle` (pre-#629 `record-codec.ts:552-554`).\n */\nexport function makeHandleProducer(keyMaterial: SealKeyMaterial): (field: string, blob: string) => SealedHandle<unknown> {\n return (field, blob) => new SealedHandle(() => unsealOneField(field, blob, keyMaterial))\n}\n\n/**\n * Restore every `[field]: blob` entry in `sealed` onto `record` (mutated in\n * place, matching the original's mutate-then-return shape): eagerly unseal\n * to plaintext (`opts.asHandles` falsy) or wrap each in a\n * {@link SealedHandle} (`opts.asHandles: true`) so the plaintext is never\n * materialised. Extracted byte-parity from `RecordCodec.decryptRecord`'s\n * sealed-handle block (pre-#629 `record-codec.ts:615-623`).\n */\nexport async function unsealFields(\n record: Record<string, unknown>,\n sealed: Record<string, string>,\n keyMaterial: SealKeyMaterial,\n opts?: { asHandles?: boolean },\n): Promise<Record<string, unknown>> {\n const makeHandle = opts?.asHandles === true ? makeHandleProducer(keyMaterial) : undefined\n for (const [field, blob] of Object.entries(sealed)) {\n record[field] = makeHandle !== undefined ? makeHandle(field, blob) : await unsealOneField(field, blob, keyMaterial)\n }\n return record\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// ViaCryptoCtx capability factories (#629 Task 1, second commit)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** What {@link makeSealedSlotCapability} needs — a subset of `RecordCodecContext`. */\nexport interface SealedSlotCapabilityCtx {\n readonly name: string\n getDEK(): Promise<EnclaveKey>\n}\n\n/**\n * Build a `ViaCryptoCtx.sealedSlots` capability pre-bound to one\n * `(collection, recordId)`. The key material (`cek`/DEK resolver) is closed\n * over and never placed on the returned object — `Object.keys`/\n * `Object.values` of the capability expose only the three methods, never a\n * key (zero-knowledge: capabilities never expose keys).\n *\n * `delete(field)` is **advisory and capability-instance-local, not real\n * erasure** — it has no store to reach and never will by itself: it just\n * adds `field` to a `Set` closed over by THIS ONE capability object, so a\n * later `unseal` call on that SAME object refuses. No I/O happens. A fresh\n * capability for the same record (e.g. `seal()`'d again, or a new\n * `makeSealedSlotCapability(...)` call) has an empty set and sees no\n * deletion at all — `seal()` on a given field also clears any prior mark\n * for it. Do not build production logic on `delete()` persisting anything.\n * The real erasure guarantee for a forgotten record is `vault.forget()`'s\n * unconditional `_writeTombstone` call, which replaces the whole live\n * envelope with a fresh, `_sealed`-free tombstone — `_sealed` disappears\n * from the store regardless of what any capability's `delete()` marked.\n * (#629 Task 10 traced this end-to-end; see task-10-report.md \"sealedSlots\n * .delete()\" section. Removing/changing this method's shape is a phase-E\n * decision, not implied by this comment.)\n */\nexport function makeSealedSlotCapability(\n ctx: SealedSlotCapabilityCtx,\n recordId: string,\n cek?: EnclaveKey,\n): ViaCryptoCtx['sealedSlots'] {\n const keyMaterial: SealKeyMaterial = {\n collection: ctx.name,\n ...(cek !== undefined ? { cek } : {}),\n getDEK: () => ctx.getDEK(),\n }\n const deletedFields = new Set<string>()\n\n return {\n async seal(field, plaintext) {\n const ref = await sealOneField(field, plaintext, keyMaterial)\n deletedFields.delete(field)\n return ref\n },\n async unseal(field, ref) {\n if (deletedFields.has(field)) {\n throw new ValidationError(`sealedSlots.unseal: field \"${field}\" on record \"${recordId}\" was deleted from this capability`)\n }\n return unsealOneField(field, `${ref.iv}:${ref.data}`, keyMaterial)\n },\n async delete(field) {\n deletedFields.add(field)\n },\n }\n}\n\n/** Resolve the DEK for an arbitrary (possibly synthetic, e.g. `_dict_en`) collection name. */\nexport type ReservedEnvelopeDekResolver = (collection: string) => Promise<EnclaveKey>\n\n/**\n * Build a `ViaCryptoCtx.reservedEnvelopes` capability: a whole-envelope\n * encrypt/decrypt door scoped to collection names under a declared prefix\n * (e.g. `_dict_`) — the shape `via/i18n/dictionary.ts`'s\n * `DictionaryHandle` needs (seam map Part 3): build JSON → encrypt under a\n * DEK → wrap in an `EncryptedEnvelope`; decrypt → JSON text. No per-record\n * CEK — reserved envelopes are whole-DEK-encrypted, same as the dictionary\n * grandfather.\n *\n * `reservedEnvelopes(prefix)` throws `ValidationError` immediately when\n * `prefix` is not in `declaredPrefixes`; each `encrypt`/`decrypt` call\n * throws `ValidationError` when its `collection` argument does not start\n * with `prefix`.\n */\nexport function makeReservedEnvelopes(\n dekResolver: ReservedEnvelopeDekResolver,\n declaredPrefixes: readonly string[],\n): ViaCryptoCtx['reservedEnvelopes'] {\n const declared = new Set(declaredPrefixes)\n\n return (prefix: string) => {\n if (!declared.has(prefix)) {\n throw new ValidationError(`reservedEnvelopes: prefix \"${prefix}\" is not declared by this binding's reservedPrefixes`)\n }\n\n const assertPrefixed = (collection: string, method: 'encrypt' | 'decrypt'): void => {\n if (!collection.startsWith(prefix)) {\n throw new ValidationError(\n `reservedEnvelopes('${prefix}').${method}: collection \"${collection}\" does not start with \"${prefix}\"`,\n )\n }\n }\n\n const encryptForPrefix = async (collection: string, json: string, v: number): Promise<EncryptedEnvelope> => {\n assertPrefixed(collection, 'encrypt')\n const dek = await dekResolver(collection)\n const { iv, data } = await encrypt(json, dek)\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: v,\n _ts: new Date().toISOString(),\n _iv: iv,\n _data: data,\n }\n }\n\n const decryptForPrefix = async (collection: string, env: EncryptedEnvelope): Promise<string> => {\n assertPrefixed(collection, 'decrypt')\n const dek = await dekResolver(collection)\n return decrypt(env._iv, env._data, dek)\n }\n\n return { encrypt: encryptForPrefix, decrypt: decryptForPrefix }\n }\n}\n","/**\n * Digest-only write engine (C6 rotate branch + notLastN ring, spec §2/§4).\n * Called ONLY by RecordCodec.encryptRecord — both live inside the enclave.\n * @module\n */\nimport { generateSalt, bufferToBase64, base64ToBuffer, type EnclaveKey } from '../crypto.js'\nimport { pbkdf2VerifyDigest, VDIG_ITERATIONS } from './digest.js'\nimport { normalizeForVerify } from './normalize.js'\nimport { blindedEqual } from './compare.js'\nimport { sealVdigPayload, openVdigPayload, type VdigPayload, type VdigDigestEntry } from './vdig.js'\nimport { ClassifiedRotationError, TamperedError } from '../../errors.js'\nimport type { VdigFieldPolicy } from '../../types.js'\n\nexport async function mintVdigSlot(\n rawValue: string,\n policy: VdigFieldPolicy,\n prevBlob: string | undefined,\n cek: EnclaveKey,\n collection: string,\n recordId: string,\n field: string,\n): Promise<string> {\n const normalized = normalizeForVerify(policy.normalize, rawValue)\n\n let prev: VdigPayload | null = null\n if (prevBlob !== undefined) {\n try {\n prev = await openVdigPayload(prevBlob, cek, collection, recordId, field)\n } catch {\n // Any failure opening a prior _vdig slot (auth-tag mismatch, malformed\n // JSON, wrong AAD) is a tamper/false condition — normalize to\n // TamperedError so the write path never sees a distinct error shape.\n throw new TamperedError()\n }\n if (!Number.isInteger(prev.iter) || prev.iter < 1) {\n // A corrupted/tampered iter count would silently weaken (or break)\n // the reuse-check PBKDF2 pass — floor/validate before trusting it.\n throw new TamperedError()\n }\n }\n\n // notLastN reuse refusal: candidate vs cur + every ring entry, each a full\n // PBKDF2 at the payload's own iteration count (n × 600K is the documented\n // write-time cost ceiling, cap 8 — spec Q4).\n if (prev !== null && policy.notLastN > 0) {\n const history: readonly VdigDigestEntry[] = [prev.cur, ...(prev.ring ?? [])]\n for (const entry of history) {\n const digest = await pbkdf2VerifyDigest(normalized, base64ToBuffer(entry.salt), prev.iter)\n if (await blindedEqual(digest, base64ToBuffer(entry.hash))) {\n throw new ClassifiedRotationError(collection, field, 'password was used recently')\n }\n }\n }\n\n const salt = generateSalt()\n const hash = await pbkdf2VerifyDigest(normalized, salt, VDIG_ITERATIONS)\n const ring = prev !== null && policy.notLastN > 0\n ? [...(prev.ring ?? []), { salt: prev.cur.salt, hash: prev.cur.hash }].slice(-policy.notLastN)\n : undefined\n\n const payload: VdigPayload = {\n v: 1,\n alg: 'PBKDF2-SHA256',\n iter: VDIG_ITERATIONS,\n cur: { salt: bufferToBase64(salt), hash: bufferToBase64(hash), at: new Date().toISOString() },\n ...(ring !== undefined && ring.length > 0 ? { ring } : {}),\n }\n return sealVdigPayload(payload, cek, collection, recordId, field)\n}\n","/**\n * Standard Schema v1 integration.\n *\n * This file is the entry point for **schema validation**. Any\n * validator that implements the [Standard Schema v1\n * protocol](https://standardschema.dev) — Zod, Valibot, ArkType, Effect\n * Schema, etc. — can be attached to a `Collection` or `defineNoydbStore`\n * and will:\n *\n * 1. Validate the record BEFORE encryption on `put()` — bad data is\n * rejected at the store boundary with a rich issue list.\n * 2. Validate the record AFTER decryption on `get()`/`list()`/`query()`\n * — stored data that has drifted from the current schema throws\n * loudly instead of silently propagating garbage to the UI.\n *\n * ## Why vendor the types?\n *\n * Standard Schema is a protocol, not a library. The spec is <200 lines of\n * TypeScript and has no runtime. There's an official `@standard-schema/spec`\n * types package on npm, but pulling it in would add a dependency edge\n * purely for type definitions. Vendoring the minimal surface keeps\n * `@noy-db/core` at **zero runtime dependencies** and gives us freedom to\n * evolve the helpers without a version-lock on the spec package.\n *\n * If the spec changes in a breaking way (unlikely — it's frozen at v1),\n * we update this file and bump our minor.\n *\n * ## Why not just run `schema.parse(value)` directly?\n *\n * Because then we'd be locked to whichever validator happens to have\n * `.parse`. Standard Schema's `'~standard'.validate` contract is the same\n * across every implementation and includes a structured issues list,\n * which is much more useful than a thrown error for programmatic error\n * handling (e.g., rendering field-level messages in a Vue component).\n */\n\nimport { SchemaValidationError } from './errors.js'\n\n/**\n * The Standard Schema v1 protocol. A schema is any object that exposes a\n * `'~standard'` property with `version: 1` and a `validate` function.\n *\n * The type parameters are:\n * - `Input` — the type accepted by `validate` (what the user passes in)\n * - `Output` — the type produced by `validate` (what we store/return,\n * may differ from Input if the schema transforms or coerces)\n *\n * In most cases `Input === Output`, but validators that transform\n * (Zod's `.transform`, Valibot's `transform`, etc.) can narrow or widen.\n *\n * We intentionally keep the `types` field `readonly` and optional — the\n * spec marks it as optional because it's only used for inference, and\n * not every implementation bothers populating it at runtime.\n */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': {\n readonly version: 1\n readonly vendor: string\n readonly validate: (\n value: unknown,\n ) =>\n | StandardSchemaV1SyncResult<Output>\n | Promise<StandardSchemaV1SyncResult<Output>>\n readonly types?:\n | {\n readonly input: Input\n readonly output: Output\n }\n | undefined\n }\n}\n\n/**\n * The result of a single call to `schema['~standard'].validate`. Either\n * `{ value }` on success or `{ issues }` on failure — never both.\n *\n * The spec allows `issues` to be undefined on success (and some\n * validators leave it that way), so consumers should discriminate on\n * `issues?.length` rather than on truthiness of `value`.\n */\nexport type StandardSchemaV1SyncResult<Output> =\n | { readonly value: Output; readonly issues?: undefined }\n | {\n readonly value?: undefined\n readonly issues: readonly StandardSchemaV1Issue[]\n }\n\n/**\n * A single validation issue. The `message` is always present; the `path`\n * is optional and points at the offending field when the schema tracks\n * it (virtually every validator does for object types).\n *\n * The path is deliberately permissive — both a plain `PropertyKey` and a\n * `{ key }` wrapper are allowed so validators that wrap path segments in\n * objects (Zod does this in some modes) don't need special handling.\n */\nexport interface StandardSchemaV1Issue {\n readonly message: string\n readonly path?:\n | ReadonlyArray<PropertyKey | { readonly key: PropertyKey }>\n | undefined\n}\n\n/**\n * Infer the output type of a Standard Schema. Consumers use this to\n * pull the type out of a schema instance when they want to declare a\n * Collection<T> or defineNoydbStore<T> with `T` derived from the schema.\n *\n * Example:\n * ```ts\n * const InvoiceSchema = z.object({ id: z.string(), amount: z.number() })\n * type Invoice = InferOutput<typeof InvoiceSchema>\n * ```\n */\nexport type InferOutput<T extends StandardSchemaV1> =\n T extends StandardSchemaV1<unknown, infer O> ? O : never\n\n/**\n * Validate an input value against a schema. Throws\n * `SchemaValidationError` if the schema rejects, with the rich issue\n * list attached. Otherwise returns the (possibly transformed) output\n * value.\n *\n * The `context` string is included in the thrown error's message so the\n * caller knows where the failure happened (e.g. `\"put(inv-001)\"`) without\n * every caller having to wrap the throw in a try/catch.\n *\n * This function is ALWAYS async because some validators (notably Effect\n * Schema and Zod's `.refine` with async predicates) can return a\n * Promise. We `await` the result unconditionally to normalize the\n * contract — the extra microtask is free compared to the cost of an\n * encrypt/decrypt round-trip.\n */\nexport async function validateSchemaInput<Output>(\n schema: StandardSchemaV1<unknown, Output>,\n value: unknown,\n context: string,\n): Promise<Output> {\n const result = await schema['~standard'].validate(value)\n if (result.issues !== undefined && result.issues.length > 0) {\n throw new SchemaValidationError(\n `Schema validation failed on ${context}: ${summarizeIssues(result.issues)}`,\n result.issues,\n 'input',\n )\n }\n // Safe: the spec guarantees `value` is present when `issues` is absent.\n return result.value as Output\n}\n\n/**\n * Validate an already-stored value coming OUT of the collection. This\n * is a distinct helper from `validateSchemaInput` because the error\n * semantics differ: an output-validation failure means the data in\n * storage has drifted from the current schema (an unexpected state),\n * whereas an input-validation failure means the user passed bad data\n * (an expected state for a UI that isn't guarding its inputs).\n *\n * We still throw — silently returning bad data would be worse — but\n * the error carries `direction: 'output'` so upstream code (and a\n * potential migrate hook) can distinguish the two cases.\n */\nexport async function validateSchemaOutput<Output>(\n schema: StandardSchemaV1<unknown, Output>,\n value: unknown,\n context: string,\n): Promise<Output> {\n const result = await schema['~standard'].validate(value)\n if (result.issues !== undefined && result.issues.length > 0) {\n throw new SchemaValidationError(\n `Stored data for ${context} does not match the current schema — ` +\n `schema drift? ${summarizeIssues(result.issues)}`,\n result.issues,\n 'output',\n )\n }\n return result.value as Output\n}\n\n/**\n * Produce a short human-readable summary of an issue list for the\n * thrown error's message. The full issue array is still attached to the\n * error as a property — this is only for the `.message` string that\n * shows up in console.error / stack traces.\n *\n * Format: `field: message; field2: message2` (up to 3 issues, then `…`).\n * Issues without a path are shown as `root: message`.\n */\nfunction summarizeIssues(\n issues: readonly StandardSchemaV1Issue[],\n): string {\n const shown = issues.slice(0, 3).map((issue) => {\n const pathStr = formatPath(issue.path)\n return `${pathStr}: ${issue.message}`\n })\n const suffix = issues.length > 3 ? ` (+${issues.length - 3} more)` : ''\n return shown.join('; ') + suffix\n}\n\nfunction formatPath(\n path: StandardSchemaV1Issue['path'],\n): string {\n if (!path || path.length === 0) return 'root'\n return path\n .map((segment) =>\n typeof segment === 'object' && segment !== null\n ? String(segment.key)\n : String(segment),\n )\n .join('.')\n}\n","/**\n * RecordCodec — the per-record envelope build + encrypt/decrypt + per-record-CEK\n * + sealed-field crypto.\n *\n * `Collection` holds one `RecordCodec` instance and delegates the crypto write\n * path (`encryptRecord` / `encryptJsonString` / `buildDebugEnvelope`), the read\n * path (`decryptRecord` / `decryptJsonString` / `resolveEnvelopeCek`), and the\n * sealed-field helpers (`unsealField` / `makeSealedHandle` / `toCacheRecord` /\n * `classifySealedShred`) to it. The codec receives every `this.*` dependency\n * it needs via {@link RecordCodecContext}.\n *\n * The crux is `cekCache`: it is the SAME `Lru` reference `Collection` owns (not\n * a copy). `resolveEnvelopeCek` reads/writes it, and tier methods +\n * `vault.invalidateRecordCaches` keep mutating that same object — a copy would\n * silently break the \"single CEK delete kills the version chain\" invariant.\n *\n * Internal service — not exported as a `@noy-db/hub/*` subpath.\n */\nimport { encrypt, decrypt, encryptDeterministic, deriveDeterministicKey, wrapCek, unwrapCek, deriveSealedFieldKeyFromCek, type EnclaveKey } from '../crypto.js'\nimport { NOYDB_FORMAT_VERSION, type EncryptedEnvelope, type CrdtMode, type CrdtState, type CrdtStrategy, type VdigFieldPolicy, type SealedHandle } from '../../types.js'\nimport { isTombstone, isDeleteMarker } from './tombstone.js'\nimport { parseSealedSlot } from './sealed-slot.js'\nimport { sealFields, unsealOneField, unsealFields, makeHandleProducer, makeSealedSlotCapability, makeReservedEnvelopes, type SealKeyMaterial } from './sealed-slots.js'\nimport { DebugReservedFieldError, ClassifiedConfigError, ValidationError } from '../../errors.js'\nimport { mintVdigSlot } from '../classify/write.js'\nimport { mintBidxTag } from '../classify/bidx.js'\nimport { normalizeForVerify } from '../classify/normalize.js'\nimport { validateSchemaOutput, type StandardSchemaV1 } from '../../schema.js'\nimport type { Lru } from '../../cache/index.js'\nimport type { ViaCryptoCtx, SealedSlotRef } from '../../via/index.js'\nimport type { ViaPipeline } from '../../via/pipeline.js'\n\n/**\n * One classified per-slot verdict from {@link RecordCodec.classifySealedShred}.\n * `shreddable` — CEK-only, tombstone makes it undecryptable. `dekResidue` —\n * collection-DEK-keyed, survives in synced/backup copies. The third class is\n * BOTH: a `_bidx` blind-index tag is live-dropped by the tombstone yet retained\n * under the surviving DEK in any pre-forget backup (honest dual accounting).\n */\nexport type SealedShredSlot = {\n readonly field: string\n readonly class: 'shreddable' | 'dekResidue' | 'live-shreddable+dekResidue-in-backups'\n}\n\n/** Everything the moving crypto methods touched on `this.*`, as a flat context. */\nexport interface RecordCodecContext<T> {\n /** Collection name — the crypto AAD scope and schema-error context. */\n readonly name: string\n /** Actor id stamped on `_by` (the collection's keyring.userId). */\n readonly actor: string\n /** False on plaintext collections — selects the no-ciphertext envelope branch. */\n readonly storeCiphertext: boolean\n /** keyring.debugPlaintext — debug-inline envelope on user collections. */\n readonly debugPlaintext: boolean\n /** Emit `_source`/`_sourceTs` provenance fields when a source is supplied. */\n readonly provenance: boolean\n /** Declared `sensitive` fields → sealed into `_sealed[field]`. */\n readonly sensitiveFields: ReadonlySet<string>\n /** Declared deterministic-index fields, or null. */\n readonly deterministicFields: ReadonlySet<string> | null\n /** Digest-only classified fields → verify policy (stage 2). Null when none. */\n readonly vdigFields: ReadonlyMap<string, VdigFieldPolicy> | null\n /**\n * C-A / R10 config-drift signal: the persisted `x-classified` marker's\n * declared digest-only field set (empty when the collection carries no\n * marker). Lazy, memoized (O(1) per handle after the first call). The R10\n * superset guard refuses whenever a field in this set is absent from\n * `vdigFields`. Consulted once per handle on any encrypted write path — the\n * cross-session, prev-free signal a fully-naive create has no `prev` for.\n * Undefined on codecs with no store access (direct-construction tests).\n */\n classifiedMarkerDigestOnly?(): Promise<readonly string[]>\n /** CRDT mode (decrypt resolves CrdtState→snapshot when set). */\n readonly crdtMode: CrdtMode | undefined\n /** CRDT strategy seam (resolveCrdtSnapshot). */\n readonly crdtStrategy: CrdtStrategy\n /** Output-schema validator, or undefined. */\n readonly schema: StandardSchemaV1<unknown, T> | undefined\n /**\n * Resolve a collection's DEK. Called with no argument for `this.name`'s\n * own DEK (every ordinary codec use); `viaCryptoCtx`'s `reservedEnvelopes`\n * door passes an explicit OTHER collection name (e.g. a reserved\n * `_dict_status` collection) so a binding's at-rest hook can address a\n * different collection's DEK without ever holding the resolver itself.\n */\n getDEK(collection?: string): Promise<EnclaveKey>\n /**\n * The collection's per-record CEK cache (SHARED reference, not a copy).\n * Ownership/lifetime stays on Collection; codec reads+writes it in\n * resolveEnvelopeCek exactly as the inline code did. `null` → no caching.\n */\n readonly cekCache: Lru<string, EnclaveKey> | null\n /**\n * Compiled Via pipeline (money, i18n, …), or undefined for a collection\n * with none declared. `encryptRecord`/`decryptRecord` consult\n * `via?.hasAtRestHooks` at the sealed-slot sub-step to choose between a\n * binding's `encodeAtRest`/`decodeAtRest` hooks and today's inline path.\n * The zero-via fast path (`via` undefined, or no binding declares an\n * at-rest hook) stays byte/behavior-identical (#629 Task 3).\n *\n * Mutable (not `readonly`, #638 Task 3): `Collection.via` can be\n * reassigned post-construction (the taint overlay, `via/graph-wiring.ts#\n * applyTaintOverlay`) — `RecordCodec.setVia` below keeps this in sync so\n * at-rest hooks read the LIVE pipeline, not a stale construction-time\n * snapshot.\n */\n via: ViaPipeline | undefined\n}\n\nexport class RecordCodec<T> {\n constructor(private readonly ctx: RecordCodecContext<T>) {}\n\n /**\n * @internal Update the live Via pipeline this codec's at-rest hooks read\n * (#638 Task 3) — called by `via/graph-wiring.ts#applyTaintOverlay` right\n * after it reassigns `Collection.via`, so `hasAtRestHooks`/`encodeAtRest`/\n * `decodeAtRest` see the newly-added `taint` binding instead of the\n * pipeline snapshot captured when this codec was constructed.\n */\n setVia(via: ViaPipeline | undefined): void {\n this.ctx.via = via\n }\n\n /** Sealed-slot key material for this codec's collection, for a given per-record CEK (or none). */\n private sealKeyMaterial(cek: EnclaveKey | undefined): SealKeyMaterial {\n return {\n collection: this.ctx.name,\n ...(cek !== undefined ? { cek } : {}),\n getDEK: () => this.ctx.getDEK(),\n }\n }\n\n /**\n * Build the `ViaCryptoCtx` handed to a binding's `encodeAtRest`/\n * `decodeAtRest` hook: `sealedSlots` pre-bound to `(this.ctx.name,\n * recordId)` — always threading `cek` when the caller has one, so the\n * capability's record-binding stays cryptographic rather than degrading\n * to collection-scope. `reservedEnvelopes`'s DEK resolver forwards its\n * `collection` argument straight to `this.ctx.getDEK` (#629 Task 4) — it\n * resolves the RESERVED collection's own DEK (e.g. `_dict_status`), never\n * `this.ctx.name`'s, matching `Vault.getDEK`'s per-collection-name\n * resolution (the same resolver `DictionaryHandle` used pre-cutover).\n */\n private viaCryptoCtx(recordId: string, cek: EnclaveKey | undefined): ViaCryptoCtx {\n const declaredPrefixes = this.ctx.via?.bindings.flatMap((b) => b.reservedPrefixes ?? []) ?? []\n return {\n sealedSlots: makeSealedSlotCapability(this.ctx, recordId, cek),\n reservedEnvelopes: makeReservedEnvelopes((collection) => this.ctx.getDEK(collection), declaredPrefixes),\n }\n }\n\n // ──────────────────────────────────────────────────────────────────────\n // Low-level literal builders\n // ──────────────────────────────────────────────────────────────────────\n\n /**\n * Assemble a canonical ciphertext body envelope literal from already-computed\n * parts. Pure, no crypto. Each call stamps its own `_ts` — provenance carries\n * a SEPARATE timestamp (computed by the caller), so the two never share one.\n */\n static buildEnvelope(p: {\n version: number\n iv: string\n data: string\n by?: string\n cek?: string\n provenance?: { source: string; sourceTs: string } | undefined\n extra?: Partial<Pick<EncryptedEnvelope, '_tier' | '_det' | '_sealed'>>\n }): EncryptedEnvelope {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: p.version,\n _ts: new Date().toISOString(),\n _iv: p.iv,\n _data: p.data,\n ...(p.by !== undefined ? { _by: p.by } : {}),\n ...(p.cek !== undefined ? { _cek: p.cek } : {}),\n ...(p.provenance !== undefined ? { _source: p.provenance.source, _sourceTs: p.provenance.sourceTs } : {}),\n ...(p.extra ?? {}),\n }\n }\n\n /** Plaintext (`_iv:''`) body envelope — the `!storeCiphertext` shape. */\n static buildPlaintextEnvelope(p: {\n version: number\n data: string\n by?: string\n provenance?: { source: string; sourceTs: string } | undefined\n }): EncryptedEnvelope {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: p.version,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: p.data,\n ...(p.by !== undefined ? { _by: p.by } : {}),\n ...(p.provenance !== undefined ? { _source: p.provenance.source, _sourceTs: p.provenance.sourceTs } : {}),\n }\n }\n\n // ──────────────────────────────────────────────────────────────────────\n // Write path\n // ──────────────────────────────────────────────────────────────────────\n\n /**\n * Build a debug-plaintext envelope: the record's own fields inlined as\n * top-level keys beside the reserved `_`-metadata, with `_debug: 1` and an\n * empty `_data`. Lets native store tooling read the record without\n * unwrapping. Only reached for user collections under `debugPlaintext`\n * (see {@link encryptRecord}). Rejects `_`-prefixed record fields, which\n * would collide with the reserved metadata namespace.\n */\n buildDebugEnvelope(record: T, version: number, source?: string, sourceTs?: string): EncryptedEnvelope {\n const rec = record as unknown as Record<string, unknown>\n for (const key of Object.keys(rec)) {\n if (key.startsWith('_')) throw new DebugReservedFieldError(this.ctx.name, key)\n }\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: '',\n _by: this.ctx.actor,\n _debug: NOYDB_FORMAT_VERSION,\n ...(this.ctx.provenance && source !== undefined ? { _source: source, _sourceTs: sourceTs ?? new Date().toISOString() } : {}),\n ...rec,\n } as unknown as EncryptedEnvelope\n }\n\n /**\n * Encrypt a JSON body into an envelope.\n *\n * When `cek` is supplied (per-record CEK collections), the body is\n * encrypted under the CEK and the CEK is AES-KW-wrapped under the\n * collection DEK and stamped on `_cek`. When `cek` is omitted, the legacy\n * path encrypts the body directly under the collection DEK — byte-identical\n * to pre-CEK behaviour, so non-adopting collections pay nothing.\n */\n async encryptJsonString(\n json: string,\n version: number,\n cek?: EnclaveKey,\n source?: string,\n sourceTs?: string,\n ): Promise<EncryptedEnvelope> {\n const by = this.ctx.actor\n const provenance = this.ctx.provenance && source !== undefined\n ? { source, sourceTs: sourceTs ?? new Date().toISOString() }\n : undefined\n\n if (!this.ctx.storeCiphertext) {\n return RecordCodec.buildPlaintextEnvelope({ version, data: json, by, provenance })\n }\n\n const dek = await this.ctx.getDEK()\n\n if (cek !== undefined) {\n const { iv, data } = await encrypt(json, cek)\n const wrapped = await wrapCek(cek, dek)\n return RecordCodec.buildEnvelope({ version, iv, data, by, cek: wrapped, provenance })\n }\n\n const { iv, data } = await encrypt(json, dek)\n return RecordCodec.buildEnvelope({ version, iv, data, by, provenance })\n }\n\n async encryptRecord(\n record: T,\n version: number,\n cek?: EnclaveKey,\n source?: string,\n sourceTs?: string,\n vdig?: { readonly id: string; readonly prev: EncryptedEnvelope | null },\n id?: string,\n ): Promise<EncryptedEnvelope> {\n // Debug-plaintext: write user-collection records with their fields inlined\n // beside the envelope metadata so native store tools read them directly.\n // Internal (`_`-prefixed) collections keep the classic shape — some store\n // `_`-prefixed fields that the inline layout would collide with.\n if (!this.ctx.storeCiphertext && this.ctx.debugPlaintext && !this.ctx.name.startsWith('_')) {\n return this.buildDebugEnvelope(record, version, source, sourceTs)\n }\n\n // ── C-A / R10 config-drift guard (superset) ────────────────────────\n // A handle may write a classified collection only if its DECLARED\n // digest-only set is a SUPERSET of the collection's stored/marked\n // classified set. Any classified field this handle does NOT declare is a\n // field it cannot maintain: the `_vdig` loop below iterates only declared\n // fields, so an undeclared classified field is neither stripped nor tagged —\n // its value would be serialized into `_data` as DEK-recoverable plaintext\n // (the C-A leak), or its stored tag silently dropped. Fail loud.\n //\n // This subsumes the old naive-only guard (a fully-naive handle has an empty\n // declared set, so ANY stored classified field violates the superset) AND\n // closes the partial-handle door (declares Y, not X, over a record carrying\n // `_vdig[X]`/`_bidx[X]`): the narrow `vdigFields === null || size === 0`\n // gate skipped that entire case. The stored/marked set is the union of:\n // 1. the persisted `x-classified` marker's declared digest-only set\n // (cross-session, prev-free — the only signal a fully-naive create has);\n // 2. the keys the target `prev` already carries in `_vdig`/`_bidx` (the\n // overwrite arm; a classified-declaring handle DOES pass `prev`).\n // Only encrypted collections can be classified, so plaintext collections skip.\n if (this.ctx.storeCiphertext) {\n const declared = this.ctx.vdigFields\n const storedClassified = new Set<string>()\n if (vdig?.prev?._vdig) for (const k of Object.keys(vdig.prev._vdig)) storedClassified.add(k)\n if (vdig?.prev?._bidx) for (const k of Object.keys(vdig.prev._bidx)) storedClassified.add(k)\n if (this.ctx.classifiedMarkerDigestOnly !== undefined) {\n for (const k of await this.ctx.classifiedMarkerDigestOnly()) storedClassified.add(k)\n }\n let undeclared: string | undefined\n for (const field of storedClassified) {\n if (declared === null || !declared.has(field)) { undeclared = field; break }\n }\n if (undeclared !== undefined) {\n throw new ClassifiedConfigError(\n this.ctx.name,\n `this collection has classified digest-only field \"${undeclared}\" but this handle does not declare it — ` +\n 'refusing to write (would drop its tag or serialize the secret as plaintext)',\n )\n }\n }\n\n // Structural group-encryption: peel declared sensitive fields out\n // of the record BEFORE building `_data`, sealing each into its own\n // `_sealed[field]` slot under a per-field key. Default-off — with no\n // sensitive fields the open record is unchanged and no `_sealed` is\n // emitted, so the envelope stays byte-identical to legacy output.\n // A collection whose via pipeline declares at-rest hooks (#629 Task 3)\n // seals through THOSE instead — the inline `sensitiveFields` path and\n // the hook path are mutually exclusive per collection, gated on\n // `hasAtRestHooks` (the inline path now serves only the plain\n // `sensitive: [...]` collection option; since #629, `classifiedFields`-\n // declared fields seal through their own via-binding hook, and since\n // #638 so does any field the taint graph seals — `via/classified/\n // binding.ts` and `kernel/via/taint-binding.ts`, respectively). The\n // zero-via fast path (no `via`, or a pipeline with no at-rest hooks,\n // e.g. money-only) always takes the `else if` branch, unchanged.\n let openRecord = record\n let sealed: Record<string, string> | undefined\n if (this.ctx.storeCiphertext && this.ctx.via?.hasAtRestHooks) {\n if (id === undefined) {\n throw new Error(\n `RecordCodec.encryptRecord: collection \"${this.ctx.name}\" has via at-rest hooks but this write ` +\n 'path supplied no record id (needed to scope the sealed-slot capability) — caller bug.',\n )\n }\n const src = record as unknown as Record<string, unknown>\n const result = await this.ctx.via.encodeAtRest(src, this.viaCryptoCtx(id, cek))\n openRecord = result.record as unknown as T\n if (result.sealed !== undefined) {\n sealed = {}\n for (const [field, ref] of Object.entries(result.sealed)) sealed[field] = `${ref.iv}:${ref.data}`\n }\n } else if (this.ctx.storeCiphertext && this.ctx.sensitiveFields.size > 0) {\n const src = record as unknown as Record<string, unknown>\n const result = await sealFields(src, this.ctx.sensitiveFields, this.sealKeyMaterial(cek))\n if (result.sealed !== undefined) {\n sealed = result.sealed\n openRecord = result.openRecord as unknown as T\n }\n }\n\n // ── Digest-only classified fields (stage 2, C6) ────────────────────\n // Per field, exactly one of: carry-forward (absent) / rotate (string) /\n // clear (null) / loud error (anything else). Runs on a CLONE so the\n // caller's record object is never mutated.\n let vdigOut: Record<string, string> | undefined\n let bidxOutMap: Record<string, string> | undefined\n if (this.ctx.vdigFields !== null && this.ctx.vdigFields.size > 0 && this.ctx.storeCiphertext) {\n if (vdig === undefined) {\n throw new Error(\n `RecordCodec.encryptRecord: collection \"${this.ctx.name}\" declares digest-only classified ` +\n `fields but this write path supplied no { id, prev } context — it would silently destroy _vdig (C6). Caller bug.`,\n )\n }\n if (cek === undefined) {\n throw new Error(\n `RecordCodec.encryptRecord: digest-only fields require a per-record CEK (R1 invariant) — ` +\n `collection \"${this.ctx.name}\" wrote without one. Caller bug.`,\n )\n }\n const open: Record<string, unknown> = { ...(openRecord as unknown as Record<string, unknown>) }\n const out: Record<string, string> = {}\n // _bidx (blind-index equality tags) rides the SAME iteration as _vdig,\n // so the structural invariant _bidx[field] ⇒ _vdig[field] holds by\n // construction: a tag is only ever written where the _vdig slot is\n // written/carried. DEK is fetched lazily — only an equatable rotate needs it.\n const bidxOut: Record<string, string> = {}\n let bidxDek: EnclaveKey | undefined\n for (const [field, policy] of this.ctx.vdigFields) {\n const value = open[field]\n const prevBlob = vdig.prev?._vdig?.[field]\n if (vdig.prev?._sealed?.[field] !== undefined) {\n // R6 transition evidence: never silently delete recoverable\n // plaintext. Checked BEFORE the value-type dispatch so the\n // carry-forward (absent) and clear (null) branches refuse too —\n // both would otherwise drop the `_sealed` slot from the new\n // envelope, silently destroying the recoverable ciphertext.\n throw new ClassifiedConfigError(\n this.ctx.name,\n `field \"${field}\" carries a recoverable _sealed slot from a previous storage form — ` +\n `recoverable ↔ digest-only transitions are refused (R6); migrate explicitly`,\n )\n }\n if (value === undefined) {\n // 1. carry-forward: verbatim bytes (CEK version-stable, AAD _v-free;\n // byte-identity keeps the ledger payload hash deterministic).\n // I-3 monotonic carry: whenever the _vdig slot is carried, ALSO copy\n // prev._bidx[field] verbatim — regardless of THIS handle's equatable\n // knob. An unrelated put by a handle with equatable removed must not\n // drop another handle's tag (anti-flip-flop). If prev has no tag,\n // there is nothing to mint from (no plaintext) → stays uncovered.\n if (prevBlob !== undefined) {\n out[field] = prevBlob\n const prevTag = vdig.prev?._bidx?.[field]\n if (prevTag !== undefined) bidxOut[field] = prevTag\n }\n continue\n }\n if (value === null) {\n // 3. clear: the defined deletion short of forget().\n delete open[field]\n continue\n }\n if (typeof value !== 'string') {\n // 4. caller bug, fail-loud.\n throw new ValidationError(\n `digest-only classified field \"${field}\" in \"${this.ctx.name}\" must be a string (rotate) or null (clear), got ${typeof value}`,\n )\n }\n // 2. rotate: validate ran in the stage-1 write seam; digest + ring here.\n out[field] = await mintVdigSlot(value, policy, prevBlob, cek, this.ctx.name, vdig.id, field)\n // _bidx branch 2: if this handle is equatable, mint a fresh tag through\n // the SAME normalization pipeline mintVdigSlot uses (normalizeForVerify)\n // — the tag provably equals computeBidxTarget for this value. If the\n // knob is OFF, emit nothing: the stale prev tag points at the now-\n // superseded value (carrying it would be wrong) and minting is disallowed\n // (knob off) → the tag is dropped. Confirm-by-verify keeps this sound.\n if (policy.equatable === true) {\n if (bidxDek === undefined) bidxDek = await this.ctx.getDEK()\n const normalized = normalizeForVerify(policy.normalize, value)\n bidxOut[field] = await mintBidxTag(normalized, bidxDek, this.ctx.name, field)\n }\n delete open[field] // strip from _data — digest-only never persists plaintext\n }\n openRecord = open as unknown as T\n if (Object.keys(out).length > 0) vdigOut = out\n if (Object.keys(bidxOut).length > 0) bidxOutMap = bidxOut\n }\n\n const base = await this.encryptJsonString(JSON.stringify(openRecord), version, cek, source, sourceTs)\n const withSealed = sealed ? { ...base, _sealed: sealed } : base\n const withVdig = vdigOut ? { ...withSealed, _vdig: vdigOut } : withSealed\n const withBidx = bidxOutMap ? { ...withVdig, _bidx: bidxOutMap } : withVdig\n if (!this.ctx.deterministicFields || !this.ctx.storeCiphertext) return withBidx\n\n // compute deterministic-ciphertext slots for every\n // declared field. Non-primitive values are JSON-stringified so\n // objects/arrays still dedupe on structural equality. Sealed fields are\n // excluded — they live only in `_sealed`, never the `_det` index.\n // L-1: `_det` encrypts under a dedicated HKDF-derived key (salt\n // `noydb-det`), never the raw DEK — the DEK's randomized-IV `_data`\n // regime and `_det`'s deterministic-IV regime must not share a key.\n const dek = await this.ctx.getDEK()\n const detKey = await deriveDeterministicKey(dek)\n const rec = record as unknown as Record<string, unknown>\n const det: Record<string, string> = {}\n for (const field of this.ctx.deterministicFields) {\n if (this.ctx.sensitiveFields.has(field)) continue\n if (this.ctx.vdigFields?.has(field)) continue // I5: digest-only never equality-correlatable\n const value = rec[field]\n if (value === undefined || value === null) continue\n const plaintext = typeof value === 'string' ? value : JSON.stringify(value)\n const { iv, data } = await encryptDeterministic(plaintext, detKey, `${this.ctx.name}/${field}`)\n det[field] = `${iv}:${data}`\n }\n if (Object.keys(det).length === 0) return withBidx\n return { ...withBidx, _det: det }\n }\n\n // ──────────────────────────────────────────────────────────────────────\n // Read path\n // ──────────────────────────────────────────────────────────────────────\n\n /**\n * Resolve the per-record CEK for a stored envelope, or `undefined` for a\n * legacy (`_cek`-absent) envelope. Unwraps `_cek` under the collection DEK and\n * memoises it in the CEK cache under `id` (when supplied) so repeated reads of\n * the same record skip the unwrap. Shared by the body-decrypt path\n * ({@link decryptJsonString}) and the sealed-field path ({@link decryptRecord}\n * / {@link toCacheRecord}) so both agree on the record's key.\n */\n async resolveEnvelopeCek(envelope: EncryptedEnvelope, id?: string): Promise<EnclaveKey | undefined> {\n if (envelope._cek === undefined) return undefined\n const cached = id !== undefined ? this.ctx.cekCache?.get(id) : undefined\n if (cached !== undefined) return cached\n const dek = await this.ctx.getDEK()\n const cek = await unwrapCek(envelope._cek, dek)\n if (id !== undefined) this.ctx.cekCache?.set(id, cek, 1)\n return cek\n }\n\n /**\n * Low-level: decrypt an envelope and return the raw JSON string.\n *\n * `_cek` presence is the format discriminant (NOT `this.perRecordCek`),\n * so a mixed vault — and a recipient that never opted into\n * `perRecordKeys` — decrypts both legacy and CEK records:\n * - `_cek` present → unwrap the CEK under the collection DEK, decrypt the\n * body under the CEK (cache the unwrapped CEK so repeated reads skip it).\n * - `_cek` absent → legacy path, body decrypts directly under the\n * collection DEK.\n *\n * The optional `id` lets reads populate the CEK cache; it is omitted by\n * callers (history, conflict merge) that have only the envelope.\n */\n async decryptJsonString(envelope: EncryptedEnvelope, id?: string): Promise<string | null> {\n // RISK #1 (forget cascade): a shred tombstone carries `_data: ''` and no\n // `_cek`. Decrypting it would call `decrypt('', '', dek)` → AES-GCM\n // OperationError → TamperedError. Return null so every read callsite\n // treats it as \"absent / skip\", matching how get()/list already drop\n // tombstones. Legacy plaintext collections (`!this.storeCiphertext`) legitimately\n // have empty `_iv`/`_data`, so `isTombstone` is false for them — preserved.\n if (isTombstone(envelope, this.ctx.storeCiphertext) || isDeleteMarker(envelope)) return null\n if (!this.ctx.storeCiphertext) {\n // Debug-plaintext layout: record fields were inlined as top-level keys\n // (see buildDebugEnvelope). Reconstruct the record from the non-`_`\n // keys. Self-describing via `_debug`, so a classic plaintext reader\n // handles debug-written envelopes too.\n if (envelope._debug !== undefined) {\n const rec: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(envelope)) {\n if (!key.startsWith('_')) rec[key] = value\n }\n return JSON.stringify(rec)\n }\n // #598: a real record never serializes to '' (JSON.stringify's floor is\n // '{}'), so an empty `_data` here is the sync engine's shape-only\n // tombstone (`isTombstoneShape` in team/sync.ts) landing on an\n // unencrypted collection, where the encryption-gated `isTombstone()`\n // check above doesn't fire. Treat it the same as an encrypted\n // tombstone: null, not a `JSON.parse('')` crash.\n return envelope._data === '' ? null : envelope._data\n }\n const cek = await this.resolveEnvelopeCek(envelope, id)\n if (cek !== undefined) return decrypt(envelope._iv, envelope._data, cek)\n const dek = await this.ctx.getDEK()\n return decrypt(envelope._iv, envelope._data, dek)\n }\n\n /**\n * Unseal a single `_sealed[field]` slot to its plaintext value: derive the\n * per-field key off the collection DEK, AES-GCM-decrypt the `iv:data` blob,\n * and JSON-parse the result. Shared by both the inline-decrypt path and a\n * {@link Sealed} handle's `reveal()` — so the on-demand reveal and the eager\n * materialisation always agree byte-for-byte.\n */\n async unsealField(field: string, blob: string, cek?: EnclaveKey): Promise<unknown> {\n // Dual-read. Current writes seal under a key derived from the record's\n // per-record CEK; legacy records (even ones whose body is\n // CEK-encrypted) are sealed under the collection-DEK key. Try the CEK key\n // first; on its AES-GCM auth failure, fall back to the DEK key. Without this\n // fallback every legacy `_sealed` record would throw TamperedError (data loss).\n return unsealOneField(field, blob, this.sealKeyMaterial(cek))\n }\n\n /**\n * Build the `ViaCryptoCtx` for forget()'s per-ref via-erase fold (#629\n * Task 10) — resolves the live envelope's CEK exactly like\n * `decryptRecord` does, then delegates to the same capability factory\n * `encodeAtRest`/`decodeAtRest` use.\n */\n async eraseCryptoCtx(id: string, live: EncryptedEnvelope): Promise<ViaCryptoCtx> {\n return this.viaCryptoCtx(id, await this.resolveEnvelopeCek(live, id))\n }\n\n /**\n * Classify a live envelope's `_sealed` slots for crypto-shred completeness.\n * `forget()` drops `_cek`/`_sealed` but\n * RETAINS the collection DEK, so only a slot keyed off the per-record CEK is\n * genuinely shredded by the tombstone; a legacy slot keyed off the\n * collection DEK survives in any synced/backup copy.\n *\n * Mirrors {@link unsealField}'s dual-read split: try the CEK-derived key per\n * slot — success → `shreddable`, AES-GCM auth failure → `dekResidue`. With no\n * `_cek` (pure legacy collection-DEK sealing) ALL slots are residue.\n */\n async classifySealedShred(\n live: EncryptedEnvelope,\n ): Promise<{ readonly slots: readonly SealedShredSlot[] }> {\n const slots: SealedShredSlot[] = []\n // Verify-digest slots are CEK-only by construction (I3): dropping `_cek`\n // makes every `_vdig[field]` permanently undecryptable — shreddable\n // unconditionally, no vdig-dekResidue class (spec §2 forget()). Same\n // honesty caveats as #306 D5 for synced/backup copies of the ciphertext.\n // A `_vdig` field ALSO carrying a `_bidx` tag is the third category: the\n // tombstone drops it from the live store, but a pre-forget backup retains\n // the blind-index tag under the surviving collection DEK — live-shreddable\n // yet dekResidue-in-backups. `_bidx[field] ⇒ _vdig[field]` (I4), so we\n // decide per vdig field and emit exactly one slot each (count-preserving).\n if (live._vdig !== undefined && live._cek !== undefined) {\n for (const field of Object.keys(live._vdig)) {\n slots.push({\n field,\n class: live._bidx?.[field] !== undefined\n ? 'live-shreddable+dekResidue-in-backups'\n : 'shreddable',\n })\n }\n }\n const sealed = live._sealed\n if (sealed === undefined) return { slots }\n const cek = await this.resolveEnvelopeCek(live)\n for (const [field, blob] of Object.entries(sealed)) {\n if (cek === undefined) { slots.push({ field, class: 'dekResidue' }); continue }\n const { iv, data } = parseSealedSlot(blob)\n try {\n await decrypt(iv, data, await deriveSealedFieldKeyFromCek(cek, this.ctx.name, field))\n slots.push({ field, class: 'shreddable' })\n } catch {\n slots.push({ field, class: 'dekResidue' })\n }\n }\n return { slots }\n }\n\n /**\n * Build a non-leaking {@link Sealed} handle over a sealed field's ciphertext.\n * The handle captures only the ciphertext `blob` and a closure to\n * {@link unsealField}; the plaintext is never stored on it — so the handle\n * may sit in the working-set cache (or be logged/serialised) without\n * exposing the value, which decrypts only on `reveal()`.\n */\n makeSealedHandle(field: string, blob: string, cek?: EnclaveKey): SealedHandle<unknown> {\n return makeHandleProducer(this.sealKeyMaterial(cek))(field, blob)\n }\n\n /**\n * Replace every field the just-written envelope actually sealed with a\n * {@link Sealed} handle, leaving every other field as its plaintext value.\n * Used to populate the cache on the write path without ever materialising\n * sealed plaintext into it. Returns `record` untouched when nothing was\n * sealed. Keys off `envelope._sealed` itself (#642 fix) — NOT\n * `this.ctx.sensitiveFields`, which only names the inline `sensitive:[...]`/\n * classified-recoverable field set: a collection whose sealing comes\n * entirely from the `taint` via-binding (a derivation/MV output or rollup\n * target folded sealed from a classified SOURCE collection, #642) declares\n * no local `sensitiveFields` at all, so that stale gate skipped this\n * conversion and left a just-written taint-sealed field as cached\n * plaintext — read back correctly ONLY after a cold cache miss (`get()`'s\n * own `decodeAtRest` call, unaffected by this gate) forced a fresh decrypt.\n */\n async toCacheRecord(record: T, envelope: EncryptedEnvelope, id?: string): Promise<T> {\n const sealed = envelope._sealed\n if (sealed === undefined || !this.ctx.storeCiphertext) {\n return record\n }\n const cek = await this.resolveEnvelopeCek(envelope, id)\n const clone = { ...(record as unknown as Record<string, unknown>) }\n for (const [field, blob] of Object.entries(sealed)) {\n clone[field] = this.makeSealedHandle(field, blob, cek)\n }\n return clone as unknown as T\n }\n\n /**\n * Apply `_sealed`-slot post-processing to an ALREADY-DECRYPTED `record`:\n * restore each `_sealed[field]` blob inline (`sealedAsHandles` falsy) or\n * as an opaque {@link Sealed} handle (`sealedAsHandles: true`), routing\n * through a via pipeline's `decodeAtRest` hook when one is declared.\n * Extracted byte-parity from `decryptRecord`'s inline block (#635) so a\n * caller that resolves its OWN per-record CEK under a DIFFERENT DEK than\n * this codec's `this.ctx.getDEK()` — namely the tier>0 `getAtTier` leg in\n * `with-audit/tiers/index.ts`, whose `_cek` is wrapped under the TIER DEK,\n * not the collection DEK `decryptRecord`/`resolveEnvelopeCek` always\n * unwrap under — can still get correct sealed-slot handling without\n * routing its whole read through `decryptRecord` (which would resolve the\n * wrong DEK for it).\n *\n * Zero-knowledge: takes only the already-resolved per-record `cek` (or\n * `undefined` for a legacy/no-CEK record) — the same shape `makeSealedHandle`\n * already takes above. Never the keyring, never a raw DEK: the legacy\n * fallback key stays fully internal to `sealKeyMaterial`/`viaCryptoCtx`,\n * which close over THIS codec's own `getDEK` (permanently bound to\n * `this.ctx.name`'s tier-0 DEK at construction) — so even when called from\n * a tier>0 context, the legacy-fallback key resolution is correct: sealed\n * fields are always sealed under the CEK or the collection's tier-0 DEK,\n * never a tier DEK (tier writes don't seal fields; `_sealed` reaches a\n * tiered envelope only via a tier-0 write later elevated).\n */\n async applySealedSlots(\n record: T,\n sealed: Record<string, string>,\n cek: EnclaveKey | undefined,\n opts: { id?: string; sealedAsHandles?: boolean } = {},\n ): Promise<T> {\n if (this.ctx.via?.hasAtRestHooks) {\n if (opts.id === undefined) {\n throw new Error(\n `RecordCodec.decryptRecord: collection \"${this.ctx.name}\" has via at-rest hooks but this read ` +\n 'path supplied no record id (needed to scope the sealed-slot capability) — caller bug.',\n )\n }\n const sealedRefs: Record<string, SealedSlotRef> = {}\n for (const [field, blob] of Object.entries(sealed)) {\n const { iv, data } = parseSealedSlot(blob)\n sealedRefs[field] = { iv, data }\n }\n return await this.ctx.via.decodeAtRest(\n record as unknown as Record<string, unknown>,\n sealedRefs,\n this.viaCryptoCtx(opts.id, cek),\n { asHandles: opts.sealedAsHandles === true },\n ) as unknown as T\n }\n return await unsealFields(\n record as unknown as Record<string, unknown>,\n sealed,\n this.sealKeyMaterial(cek),\n { asHandles: opts.sealedAsHandles === true },\n ) as unknown as T\n }\n\n /**\n * Decrypt an envelope under an EXPLICIT `dek` instead of this codec's own\n * `ctx.getDEK()` / cekCache resolution — for a from-tier>0 pre-move decode\n * (`with-audit/tiers/index.ts`'s `putAtTier`/`elevate`/`demote` sites,\n * #722 whole-branch review). Mirrors the manual unwrap-then-decrypt\n * `getAtTier`'s own tier>0 leg performs there (same primitives, same\n * `applySealedSlots` extraction point #635 built for exactly this caller).\n *\n * `decryptRecord` is tier-UNAWARE: `resolveEnvelopeCek` reads the cekCache\n * or falls back to `ctx.getDEK()` — this codec's DEFAULT (tier-0/\n * collection) DEK — never the envelope's OWN tier. That only happens to\n * decrypt a from-tier>0 envelope correctly when some other call in the\n * SAME op already primed the cekCache with this record's CEK (true for an\n * ordinary put()-then-elevate(0→1); NOT true for a `putAtTier`-origin\n * envelope, whose body is encrypted directly under its tier DEK with no\n * `_cek` ever minted, or a non-`perRecordKeys` collection, which has no\n * `_cek` at all) — those throw `TamperedError`. Decrypting under the\n * caller-resolved `dek` is correct regardless of cache state or\n * `perRecordKeys`.\n *\n * No CRDT resolution, no schema validation — same scope as `getAtTier`'s\n * tier>0 branch (the established precedent for a tier-aware read), which\n * skips both for the same reason: a tier>0 read is an internal/derived-\n * output-recompute path, not the public per-record read surface schema\n * validation guards.\n */\n async decryptRecordAtDek(envelope: EncryptedEnvelope, dek: EnclaveKey, id?: string): Promise<T | null> {\n if (isTombstone(envelope, this.ctx.storeCiphertext) || isDeleteMarker(envelope)) return null\n let plaintext: string\n let cek: EnclaveKey | undefined\n if (envelope._cek !== undefined) {\n cek = await unwrapCek(envelope._cek, dek)\n plaintext = await decrypt(envelope._iv, envelope._data, cek)\n } else {\n plaintext = await decrypt(envelope._iv, envelope._data, dek)\n }\n let record = JSON.parse(plaintext) as T\n if (envelope._sealed !== undefined && this.ctx.storeCiphertext) {\n record = await this.applySealedSlots(record, envelope._sealed, cek, { ...(id !== undefined ? { id } : {}), sealedAsHandles: true })\n }\n return record\n }\n\n /**\n * Decrypt an envelope into a record of type `T`.\n *\n * When a schema is attached, the decrypted value is validated before\n * being returned. A divergence between the stored bytes and the\n * current schema throws `SchemaValidationError` with\n * `direction: 'output'` — silently returning drifted data would\n * propagate garbage into the UI and break the whole point of having\n * a schema.\n *\n * `skipValidation` exists for history reads: when calling\n * `getVersion()` the caller is explicitly asking for an old snapshot\n * that may predate a schema change, so validating it would be a\n * false positive. Every non-history read leaves this flag `false`.\n */\n async decryptRecord(\n envelope: EncryptedEnvelope,\n opts: { skipValidation?: boolean; id?: string; sealedAsHandles?: boolean } = {},\n ): Promise<T | null> {\n const json = await this.decryptJsonString(envelope, opts.id)\n // Tombstone (shredded record) → null, propagated from decryptJsonString.\n // Callers skip null exactly as they already skip a tombstone envelope.\n if (json === null) return null\n let parsed: unknown = JSON.parse(json)\n\n // CRDT resolution: if this collection is in CRDT mode, the\n // stored JSON is a CrdtState, not T directly. Resolve to the snapshot.\n if (this.ctx.crdtMode && parsed !== null && typeof parsed === 'object' && '_crdt' in parsed) {\n parsed = this.ctx.crdtStrategy.resolveCrdtSnapshot(parsed as CrdtState)\n }\n\n let record = parsed as T\n\n // Structural group-encryption + sealed access gate.\n // Each `_sealed[field]` slot is restored under its own per-field key.\n // `sealedAsHandles: false` (default — internal callers that compute on\n // real values) inline-decrypts to the plaintext value; `true` (the\n // public / cache path) yields an opaque {@link Sealed} handle so the\n // plaintext is never materialised into the working-set cache. A\n // collection whose via pipeline declares at-rest hooks (#629 Task 3)\n // decodes through THOSE instead of the inline dual-read below — the\n // zero-via fast path (no `via`, or a pipeline with no at-rest hooks)\n // always takes the `else` branch, unchanged.\n if (envelope._sealed !== undefined && this.ctx.storeCiphertext) {\n const sealedCek = await this.resolveEnvelopeCek(envelope, opts.id)\n record = await this.applySealedSlots(record, envelope._sealed, sealedCek, {\n ...(opts.id !== undefined ? { id: opts.id } : {}),\n ...(opts.sealedAsHandles !== undefined ? { sealedAsHandles: opts.sealedAsHandles } : {}),\n })\n }\n\n // Skip output validation when sealed fields are returned as handles:\n // the record carries opaque `Sealed` handles in place of the declared\n // values, so a whole-record schema check would false-positive on them.\n // (The values were validated on input/write; the open fields are\n // unchanged from the validated body.) The inline-value path below still\n // validates fully.\n const sealedAsHandles = opts.sealedAsHandles === true && envelope._sealed !== undefined\n if (this.ctx.schema !== undefined && !opts.skipValidation && !sealedAsHandles) {\n // Context string deliberately avoids leaking the record id — the\n // envelope only carries the version, not the id (the id lives in\n // the adapter-side key). `<collection>@v<n>` is enough for the\n // developer to find the offending record.\n record = await validateSchemaOutput(\n this.ctx.schema,\n record,\n `${this.ctx.name}@v${envelope._v}`,\n )\n }\n\n return record\n }\n}\n","/**\n * Record-scoped CEK sealing — grantor side.\n *\n * The **grantor** holds the collection DEK and seals ONE record's CEK to an\n * `at-*` host so that host — and only that host — can decrypt exactly that\n * record, with no access to the vault DEK and no ability to read any other\n * record. This is the counterpart to the host-side `openSealedRecord`\n * (`@noy-db/hub/sealed-record`), which holds no DEK.\n *\n * These functions are the orchestration behind `vault.sealRecordToHost` /\n * `vault.revokeSealedRecord` / `vault.rotateRecordCek`, lifted off `Vault`\n * behind a narrow {@link SealingContext} so the kernel file delegates. They\n * live in `record-keys/` (the vault-side CEK policy layer), NOT in\n * `sealed-record/`, so the host-side subpath stays DEK-free — the wire\n * *types* they share are spine-owned (`kernel/types.ts`), not imported from\n * `sealed-record/` directly.\n */\nimport { encrypt, decrypt, generateDEK, wrapCek, unwrapCek, bufferToBase64, deriveSealedFieldKeyFromCek, type EnclaveKey } from '../crypto.js'\nimport {\n NOYDB_FORMAT_VERSION,\n type EncryptedEnvelope,\n type NoydbStore,\n type RecipientSealer,\n type SealedCekBinding,\n type SealedCekDeliveryEnvelope,\n} from '../../types.js'\nimport { dualReadSealedSlot } from './sealed-slot.js'\nimport { openVdigPayload, sealVdigPayload } from '../classify/vdig.js'\nimport { RecordCekNotFoundError, ValidationError } from '../../errors.js'\n\nconst subtle = globalThis.crypto.subtle\n\n/** The `_sealed_cek` delivery namespace (one envelope per `collection/id/pid`). */\nexport const SEALED_CEK_NS = '_sealed_cek'\n\n/** What the grantor functions need from their `Vault`. */\nexport interface SealingContext {\n readonly adapter: NoydbStore\n /** Vault id (the adapter's first coordinate). */\n readonly vault: string\n /** Resolve the DEK a record's CEK is wrapped under. */\n getDEK(collection: string): Promise<EnclaveKey>\n /** Actor id stamped on `_by` (empty string → omit). */\n readonly actor: string\n /** Evict the per-record CEK cache + decrypted-record cache after a rotation. */\n invalidateRecordCaches(collection: string, id: string): Promise<void>\n}\n\n/**\n * Seal a record's CEK to a host. See `vault.sealRecordToHost` for the contract.\n * Returns `{ pid, envelopeKey }`.\n */\nexport async function sealRecordToHost(\n ctx: SealingContext,\n collection: string,\n id: string,\n hostSealer: RecipientSealer,\n opts: { expiresAt: string },\n): Promise<{ pid: string; envelopeKey: string }> {\n // Guard separators: the `_sealed_cek` id is `collection/id/pid`, so a `/` in\n // any component would make the prefix-delete in rotateRecordCek() (which\n // matches `${collection}/${id}/`) ambiguous and could over- or under-match.\n if (collection.includes('/')) throw new ValidationError(`sealRecordToHost: collection \"${collection}\" must not contain \"/\"`)\n if (id.includes('/')) throw new ValidationError(`sealRecordToHost: id \"${id}\" must not contain \"/\"`)\n // M-4: reject a malformed/empty expiry at seal time. `Date.parse('')` (and any\n // non-ISO string) is NaN, and the open-time check `NaN <= now` is `false` — so\n // a malformed expiry would silently mint an ETERNAL grant. A *past* (but valid)\n // timestamp is still allowed: it seals, then fails closed at open time.\n if (Number.isNaN(Date.parse(opts.expiresAt))) {\n throw new ValidationError(`sealRecordToHost: expiresAt \"${opts.expiresAt}\" is not a valid ISO 8601 timestamp`)\n }\n\n const live = await ctx.adapter.get(ctx.vault, collection, id)\n if (!live || live._cek === undefined) {\n throw new RecordCekNotFoundError(collection, id)\n }\n\n const dek = await ctx.getDEK(collection)\n const cek = await unwrapCek(live._cek, dek)\n const rawCek = await subtle.exportKey('raw', cek)\n const cekB64 = bufferToBase64(rawCek)\n\n const hint = await hostSealer.publishRecipientHint()\n if (hint.pid.includes('/')) throw new ValidationError(`sealRecordToHost: recipient pid \"${hint.pid}\" must not contain \"/\"`)\n\n const binding: SealedCekBinding = {\n collection,\n id,\n cek: cekB64,\n expiresAt: opts.expiresAt,\n }\n const sealed = await hostSealer.sealForRecipient(\n new TextEncoder().encode(JSON.stringify(binding)),\n hint,\n )\n\n const delivery: SealedCekDeliveryEnvelope = {\n v: 1,\n _noydb_sealed_cek: 1,\n pid: hint.pid,\n payload: bufferToBase64(sealed),\n expiresAt: opts.expiresAt,\n }\n\n const envelopeKey = `${collection}/${id}/${hint.pid}`\n const prior = await ctx.adapter.get(ctx.vault, SEALED_CEK_NS, envelopeKey)\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 sealing layer is the security boundary, exactly\n // like the managed-secret `_meta/sealed-secret` envelope.\n _iv: '',\n _data: JSON.stringify(delivery),\n ...(ctx.actor ? { _by: ctx.actor } : {}),\n }\n await ctx.adapter.put(ctx.vault, SEALED_CEK_NS, envelopeKey, env)\n\n return { pid: hint.pid, envelopeKey }\n}\n\n/**\n * Revoke one sealed-CEK grant.\n *\n * **Default (`{ hard: false }`) is SOFT** — it only deletes the delivery\n * envelope from the store. A host that already fetched (or cached the unsealed\n * CEK from) that envelope KEEPS decrypt capability for the record; soft revoke\n * is a \"stop new fetches\" control, not a cryptographic cutoff.\n *\n * **`{ hard: true }`** additionally rotates the record's CEK (via\n * {@link rotateRecordCek}): the live body is re-encrypted under a fresh CEK and\n * EVERY sealed-CEK delivery envelope for the record (all pids) is deleted, so\n * any previously-sealed CEK fails the AES-GCM auth tag on the rotated body —\n * future opens are cryptographically denied. (PRE-rotation history versions,\n * which keep their old `_cek`, remain openable — same semantics as\n * `rotateRecordCek`.)\n */\nexport async function revokeSealedRecord(\n ctx: SealingContext,\n collection: string,\n id: string,\n pid: string,\n opts?: { hard?: boolean },\n): Promise<void> {\n if (opts?.hard === true) {\n // Hard revocation supersedes the single-pid soft delete: rotateRecordCek\n // re-keys the record and drops every delivery envelope for it.\n await rotateRecordCek(ctx, collection, id)\n return\n }\n await ctx.adapter.delete(ctx.vault, SEALED_CEK_NS, `${collection}/${id}/${pid}`)\n}\n\n/**\n * HARD-rotate a record's CEK: re-encrypt the live body under a fresh CEK, evict\n * caches, and delete EVERY sealed-CEK delivery envelope for the record. See\n * `vault.rotateRecordCek` for why this bypasses `Collection.put` (no guards, no\n * history bump — a key rotation must not re-encrypt prior history under the new\n * CEK).\n */\nexport async function rotateRecordCek(\n ctx: SealingContext,\n collection: string,\n id: string,\n): Promise<void> {\n const live = await ctx.adapter.get(ctx.vault, collection, id)\n if (!live || live._cek === undefined) {\n throw new RecordCekNotFoundError(collection, id)\n }\n\n const dek = await ctx.getDEK(collection)\n const oldCek = await unwrapCek(live._cek, dek)\n const json = await decrypt(live._iv, live._data, oldCek)\n\n const newCek = await generateDEK()\n const { iv, data } = await encrypt(json, newCek)\n\n // Sealed fields are keyed off the per-record CEK, so a rotation\n // must RE-ENCRYPT each `_sealed[field]` under the new CEK (carrying the old\n // slots forward would orphan them — the old CEK is gone after this rotate).\n // Dual-read the old slot: try the old-CEK-derived key, fall\n // back to the collection-DEK key (legacy), then re-seal under newCek.\n let sealedOut: Record<string, string> | undefined\n if (live._sealed !== undefined) {\n const out: Record<string, string> = {}\n for (const [field, slot] of Object.entries(live._sealed)) {\n const plaintext = await dualReadSealedSlot(slot, field, collection, oldCek, dek)\n const resealed = await encrypt(plaintext, await deriveSealedFieldKeyFromCek(newCek, collection, field))\n out[field] = `${resealed.iv}:${resealed.data}`\n }\n sealedOut = out\n }\n\n // Verify-digest slots are keyed off the per-record CEK too (I3: CEK-only,\n // no DEK fallback → single read, not a dual-read). Rotation must re-seal\n // each `_vdig[field]` under the new CEK with the SAME reconstructed AAD, or\n // the correct password would false-reject forever (C3).\n let vdigOut: Record<string, string> | undefined\n if (live._vdig !== undefined) {\n const out: Record<string, string> = {}\n for (const [field, blob] of Object.entries(live._vdig)) {\n const payload = await openVdigPayload(blob, oldCek, collection, id, field)\n out[field] = await sealVdigPayload(payload, newCek, collection, id, field)\n }\n vdigOut = out\n }\n\n const env: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: live._v + 1,\n _ts: new Date().toISOString(),\n _iv: iv,\n _data: data,\n _cek: await wrapCek(newCek, dek),\n ...(ctx.actor ? { _by: ctx.actor } : {}),\n ...(live._tier !== undefined ? { _tier: live._tier } : {}),\n ...(live._det !== undefined ? { _det: live._det } : {}),\n // `_bidx` (equality-index tag) is DEK-rooted and CEK-independent, so it is\n // carried forward VERBATIM — not recomputed — unlike `_vdig`/`_sealed`\n // below, which are CEK-bound and must be re-sealed under the new CEK.\n // Dropping this line would silently orphan the equality index on rotation\n // (the #306 Slice-A data-loss bug, replayed on the index).\n ...(live._bidx !== undefined ? { _bidx: live._bidx } : {}),\n // Re-encrypt sealed (`sensitive`) fields under the new CEK. Sealed\n // keys derive off the per-record CEK, so the rotated record's old CEK is\n // destroyed here — carrying the old `_sealed` slots forward verbatim would\n // orphan them (unreadable under the new CEK). `sealedOut` holds the slots\n // dual-read from the old CEK (or the legacy DEK) and re-sealed under newCek.\n ...(sealedOut !== undefined ? { _sealed: sealedOut } : {}),\n ...(vdigOut !== undefined ? { _vdig: vdigOut } : {}),\n }\n await ctx.adapter.put(ctx.vault, collection, id, env)\n\n // Evict BOTH caches synchronously so no read returns the stale old-CEK record.\n await ctx.invalidateRecordCaches(collection, id)\n\n // Delete every sealed-CEK delivery envelope for this record — all pids.\n const prefix = `${collection}/${id}/`\n const keys = await ctx.adapter.list(ctx.vault, SEALED_CEK_NS)\n for (const key of keys) {\n if (key.startsWith(prefix)) {\n await ctx.adapter.delete(ctx.vault, SEALED_CEK_NS, key)\n }\n }\n}\n","/**\n * Deterministic-index lookups (`findByDet` / `queryByDet`).\n *\n * A collection that declares `deterministicFields` stamps a deterministic\n * AES-GCM ciphertext for each such field on the envelope's `_det` slot at write\n * time (the encrypt side lives in {@link RecordCodec.encryptRecord}). These\n * helpers are the READ side: recompute the deterministic ciphertext for a query\n * value and scan the adapter for envelopes whose `_det[field]` matches — no\n * record bodies are decrypted during the scan, which is the whole point of a\n * deterministic index.\n *\n * Both functions take a small {@link DeterministicContext} (the exact `this.*`\n * the moving methods touched) instead of `this`, mirroring the `record-keys/`\n * siblings. Behaviour is byte-identical to the inline code they replaced.\n * Elevated records (_tier > 0) are skipped: invisible to tier-0 scans (#691).\n *\n * Internal service — not exported as a `@noy-db/hub/*` subpath.\n */\nimport { encryptDeterministic, deriveDeterministicKey, type EnclaveKey } from '../crypto.js'\nimport type { NoydbStore } from '../../types.js'\nimport type { RecordCodec } from './record-codec.js'\n\n/** Everything the moving deterministic-index methods touched on `this.*`. */\nexport interface DeterministicContext<T> {\n /** Collection name — the deterministic-encryption AAD scope. */\n readonly name: string\n /** Vault namespace the records live under. */\n readonly vault: string\n /** The ciphertext store (scanned envelope-by-envelope). */\n readonly adapter: NoydbStore\n /** Declared deterministic-index fields, or null when the feature is off. */\n readonly deterministicFields: ReadonlySet<string> | null\n /** False on plaintext collections — det lookups are encrypted-only. */\n readonly storeCiphertext: boolean\n /** The collection DEK resolver. */\n getDEK(): Promise<EnclaveKey>\n /** The record codec — decrypts a matched envelope to T. */\n readonly codec: RecordCodec<T>\n}\n\n/**\n * Recompute the deterministic ciphertext targets for a query value.\n *\n * L-1: _det migrated to a dedicated HKDF key (salt noydb-det); dual-query\n * keeps pre-migration envelopes findable — they self-heal to the new key on\n * their next write. Index 0 is the current (derived-key) target, index 1 the\n * legacy raw-DEK target. Both are derived once per query call, not per\n * envelope, so the extra derivation is a fixed cost.\n */\nasync function detTargets<T>(ctx: DeterministicContext<T>, field: string, value: unknown): Promise<[string, string]> {\n const dek = await ctx.getDEK()\n const detKey = await deriveDeterministicKey(dek)\n const plaintext = typeof value === 'string' ? value : JSON.stringify(value)\n const context = `${ctx.name}/${field}`\n const [current, legacy] = await Promise.all([\n encryptDeterministic(plaintext, detKey, context),\n encryptDeterministic(plaintext, dek, context),\n ])\n return [`${current.iv}:${current.data}`, `${legacy.iv}:${legacy.data}`]\n}\n\nfunction assertDetField<T>(ctx: DeterministicContext<T>, field: string, method: string): void {\n if (!ctx.deterministicFields || !ctx.deterministicFields.has(field)) {\n throw new Error(\n `Collection \"${ctx.name}\": field \"${field}\" is not declared in deterministicFields`,\n )\n }\n if (!ctx.storeCiphertext) {\n throw new Error(\n `Collection \"${ctx.name}\": ${method} is only meaningful on encrypted collections`,\n )\n }\n}\n\n/**\n * Find the first record whose deterministic field matches the given plaintext.\n * Returns `null` when no match exists.\n *\n * Reads every envelope via the adapter and compares the stored `_det[field]` to\n * a freshly computed deterministic ciphertext — no record bodies are decrypted\n * during the search.\n *\n * Throws when the field is not declared in `deterministicFields`, so a typo\n * fails loudly at the call site rather than silently returning null forever.\n */\nexport async function findByDet<T>(ctx: DeterministicContext<T>, field: string, value: unknown): Promise<T | null> {\n assertDetField(ctx, field, 'findByDet')\n const [target, legacyTarget] = await detTargets(ctx, field, value)\n\n const ids = await ctx.adapter.list(ctx.vault, ctx.name)\n for (const id of ids) {\n const env = await ctx.adapter.get(ctx.vault, ctx.name, id)\n // #691: an elevated record's _det slot is tier-independent (#662 carries\n // it), so it MATCHES — but its key material is tier-wrapped. Skip it\n // explicitly: invisible to tier-0 det scans regardless of cekCache state\n // (a catch-based fix would still leak plaintext, audit-free, in the\n // session that performed the elevate).\n if (!env || !env._det || (env._tier ?? 0) > 0) continue\n if (env._det[field] === target || env._det[field] === legacyTarget) {\n return ctx.codec.decryptRecord(env, { id })\n }\n }\n return null\n}\n\n/**\n * Return every record whose deterministic field matches. Same semantics as\n * {@link findByDet} but without the short-circuit.\n */\nexport async function queryByDet<T>(ctx: DeterministicContext<T>, field: string, value: unknown): Promise<T[]> {\n assertDetField(ctx, field, 'queryByDet')\n const [target, legacyTarget] = await detTargets(ctx, field, value)\n\n const ids = await ctx.adapter.list(ctx.vault, ctx.name)\n const matches: T[] = []\n for (const id of ids) {\n const env = await ctx.adapter.get(ctx.vault, ctx.name, id)\n // #691: skip elevated — see findByDet above\n if (!env || !env._det || (env._tier ?? 0) > 0) continue\n if (env._det[field] === target || env._det[field] === legacyTarget) {\n const rec = await ctx.codec.decryptRecord(env, { id })\n if (rec !== null) matches.push(rec) // skip tombstone (defensive)\n }\n }\n return matches\n}\n","/**\n * Enclave body helpers — the C1 protected-body access contract.\n *\n * `EncryptedEnvelope` splits into a protocol header (family-owned; `_noydb`,\n * `_v`, `_ts`, `_by`, `_source`, `_sourceTs`, `_tier`, `_elevatedBy`) and a\n * protected body (enclave-owned; `_iv`, `_data`, `_cek`, `_det`, `_sealed`,\n * `_debug`). The four helpers below are the ONLY sanctioned way for code\n * outside `kernel/enclave/**` to read or construct the protected body —\n * later migration batches move the ~121 direct `_iv`/`_data`/`_cek`/`_sealed`\n * access sites in `with-*` services onto these.\n *\n * Each helper reproduces an EXISTING behavior byte-for-byte (see the\n * per-function doc for its oracle call site) — this file introduces no new\n * crypto or semantics, only a narrower door onto what already runs.\n */\nimport { encrypt, decrypt, generateDEK, wrapCek, unwrapCek, type EnclaveKey } from '../crypto.js'\nimport type { EncryptedEnvelope } from '../../types.js'\n\n/**\n * Open an envelope's protected body to its JSON text.\n *\n * Mirrors the dominant direct-decrypt call-site shape (e.g.\n * `with-audit/consent/consent.ts`'s `decryptEntry`):\n * - `opts.encrypted === false` (default `true`) → plaintext collection;\n * returns `env._data` as-is, `key` untouched.\n * - `env._cek` present → per-record-key envelope (mirrors\n * `record-codec.ts`'s `resolveEnvelopeCek`): unwrap the CEK under `key`\n * (the collection DEK), decrypt the body under the unwrapped CEK.\n * - `env._cek` absent → legacy path, decrypt the body directly under `key`.\n */\nexport async function openEnvelopeJson(\n env: EncryptedEnvelope,\n key: EnclaveKey,\n opts?: { encrypted?: boolean },\n): Promise<string> {\n if (opts?.encrypted === false) return env._data\n if (env._cek !== undefined) {\n const cek = await unwrapCek(env._cek, key)\n return decrypt(env._iv, env._data, cek)\n }\n return decrypt(env._iv, env._data, key)\n}\n\n/**\n * Produce the protected-body fields (`_iv`/`_data`/`_cek`) for an envelope a\n * caller is assembling.\n *\n * - `opts.encrypted === false` (default `true`) → plaintext collection;\n * emits `{ _iv: '', _data: json }` (today's `buildPlaintextEnvelope`\n * shape), `key` untouched.\n * - `opts.perRecordKey === true` → mints a fresh per-record CEK, encrypts\n * the body under it, and AES-KW-wraps the CEK under `key` (mirrors\n * `encryptJsonString`'s `cek !== undefined` branch, except the CEK is\n * generated here rather than supplied by the caller).\n * - otherwise → legacy path, body encrypted directly under `key`.\n */\nexport async function writeEnvelopeBody(\n json: string,\n key: EnclaveKey,\n opts?: { encrypted?: boolean; perRecordKey?: boolean },\n): Promise<Pick<EncryptedEnvelope, '_iv' | '_data' | '_cek'>> {\n if (opts?.encrypted === false) return { _iv: '', _data: json }\n\n if (opts?.perRecordKey === true) {\n const cek = await generateDEK()\n const { iv, data } = await encrypt(json, cek)\n const wrapped = await wrapCek(cek, key)\n return { _iv: iv, _data: data, _cek: wrapped }\n }\n\n const { iv, data } = await encrypt(json, key)\n return { _iv: iv, _data: data }\n}\n\n/**\n * Approximate protected-body payload size for KPI/telemetry (#807's\n * period-scoped pull download counters): `_data` + `_iv` string length —\n * ≈ ciphertext bytes for base64 payloads, exact for plaintext (`_iv: ''`)\n * collections. A measuring door, not a crypto one: callers get a size\n * without touching the protected fields directly.\n */\nexport function envelopeBodySize(env: EncryptedEnvelope): number {\n return env._data.length + env._iv.length\n}\n\n/**\n * Discriminant: does this envelope carry a per-record key?\n *\n * Replaces raw `envelope._cek !== undefined` checks scattered across\n * services — `_cek` presence is the format discriminant (see\n * `record-codec.ts`'s `resolveEnvelopeCek` doc).\n */\nexport function hasPerRecordKey(env: EncryptedEnvelope): boolean {\n return env._cek !== undefined\n}\n\n/**\n * Canonical body string for the ledger hash chain — the exact bytes\n * `with-commit/history/ledger/hash.ts`'s `envelopePayloadHash` derives from\n * `_data` + `_sealed` + `_vdig` + `_bidx` before hashing:\n * - no `_sealed`, no `_vdig`, no `_bidx` → `_data` alone (back-compat: every\n * pre-existing ledger entry and non-sealed backup hashes byte-identically).\n * - any map present → canonical JSON of `{ _data, _sealed?, _vdig?, _bidx? }`\n * with sorted keys at every level, each map bound ONLY when present, so\n * the result is independent of the maps' field-insertion / store-\n * serialization order. `_bidx` is always the LAST segment, so a legacy\n * `_vdig`-only / `_bidx`-absent envelope hashes byte-identically to its\n * stage-2 value.\n *\n * Deliberately reimplements the two-key-object canonicalization inline\n * rather than importing `with-commit/history/ledger/entry.ts`'s general\n * `canonicalJson` — `kernel/enclave/**` may import only spine types (C3),\n * never a `with-*` service. For this fixed `{ _data: string; _sealed:\n * Record<string, string> }` shape the two produce byte-identical output\n * (verified against that exact call site's oracle expression in\n * `envelope-body.test.ts`).\n */\nexport function envelopeBodyForHash(env: EncryptedEnvelope): string {\n // Conditional widen (stage 2): bind `_vdig` exactly the way `_sealed` is\n // bound — only when present. No existing envelope carries `_vdig`, so this\n // ships with no flag-day PROVIDED it lands in the same slice as the first\n // `_vdig` writer (it does — Tasks 7/8/11 are one branch). This binding is\n // the temporal-rollback detector completing C1: AAD stops cross-record/\n // cross-field splices; the ledger hash catches same-slot rollbacks.\n //\n // Conditional widen (slice 2b, SM #5): bind `_bidx` the same way, appended\n // LAST (after `_vdig`) so a `_bidx`-absent envelope keeps its stage-2\n // byte-identical hash.\n if (env._sealed === undefined && env._vdig === undefined && env._bidx === undefined) return env._data\n const mapPart = (key: '_sealed' | '_vdig' | '_bidx', map: Record<string, string>): string => {\n const parts = Object.keys(map).sort().map(\n (k) => `${JSON.stringify(k)}:${JSON.stringify(map[k])}`,\n )\n return `${JSON.stringify(key)}:{${parts.join(',')}}`\n }\n const segments = [`\"_data\":${JSON.stringify(env._data)}`]\n if (env._sealed !== undefined) segments.push(mapPart('_sealed', env._sealed))\n if (env._vdig !== undefined) segments.push(mapPart('_vdig', env._vdig))\n if (env._bidx !== undefined) segments.push(mapPart('_bidx', env._bidx))\n return `{${segments.join(',')}}`\n}\n","/**\n * Credential-broker proof crypto (#479, credential-broker spec §3).\n *\n * The `_broker` seed (32 random bytes, decrypted from `_broker/<brokerId>` by\n * the party layer) is never transmitted. Instead, a **proof key** is\n * HKDF-derived from it, domain-separated per `(vaultId, brokerId)`, and the\n * derived bits are registered with the broker host once at enrol. Steady\n * state, the client re-derives the same key from the seed and signs a\n * **canonical** string binding the request's identity/context, so the broker\n * can verify a fresh HMAC on every mint without ever holding the seed itself.\n *\n * ## Derivation (spec §3, follow verbatim)\n *\n * ```\n * hkdf = importKey('raw', seed, 'HKDF', false, ['deriveBits'])\n * proofBits = deriveBits({ name:'HKDF', hash:'SHA-256',\n * salt: utf8(BROKER_PROOF_DOMAIN),\n * info: utf8(JSON.stringify([BROKER_PROOF_DOMAIN, vaultId, brokerId])) },\n * hkdf, 256)\n * proofKey = importKey('raw', proofBits, { name:'HMAC', hash:'SHA-256' }, false, ['sign'])\n * proof = base64(sign('HMAC', proofKey, utf8(canonical)))\n * ```\n *\n * `BROKER_PROOF_DOMAIN` is both the HKDF salt and the first `info` array\n * element (the injective-JSON-array convention `deriveClassifyIndexKey`\n * established — see `classify/bidx.ts`). `BROKER_PROOF_VERSION` is the\n * separate MAC version tag, the first element of the `canonical` array.\n *\n * ## Canonical (spec §2 step 3)\n *\n * `JSON.stringify([BROKER_PROOF_VERSION, vaultId, endpointOrigin, brokerId,\n * profile ?? '', instancePid ?? '', challenge, expiresAt])` — every binding\n * field folded into ONE MAC input:\n * - `vaultId` (V1), `challenge` (V3), `expiresAt` (V4) — verbatim strings,\n * never reparsed/reformatted (F8): a byte-identical ISO string is\n * required, so e.g. `Z` vs `+00:00` for the same instant fails.\n * - `profile ?? ''` (F1) — a proof captured at one profile cannot be\n * replayed at another.\n * - `endpointOrigin` (F4) — a proof cannot be relayed to a different broker\n * endpoint reusing the same `brokerId`.\n * - `instancePid ?? ''` (F7) — an EMPTY STRING `instancePid` is forbidden in\n * both {@link computeBrokerProof} and {@link verifyBrokerProof}: absent\n * (the field omitted) and `''` must stay distinguishable, or `?? ''`\n * would alias \"no instance\" onto a real registered instance.\n *\n * ## Verify order (spec §3, non-negotiable, F2/F6)\n *\n * {@link verifyBrokerProof} MUST, in this exact order: (1) burn the challenge\n * via `consumeChallenge` FIRST and short-circuit `false` before any MAC work\n * (F2 — burn-on-presentation, not burn-on-success); (2) recompute the\n * canonical and compare with `crypto.subtle.verify('HMAC', …)` — portable,\n * constant-time, never a manual byte compare or Node `timingSafeEqual` (F6);\n * (3) assert `now < expiresAt` — the client clock is never trusted, so this\n * check only matters once the MAC (which covers `expiresAt`) has verified.\n *\n * ## Zeroing (F5)\n *\n * `seed` and the transient HKDF output are raw, durable secret material —\n * the `recovery.ts:276-307` `try{}finally{secret.fill(0)}` discipline, not\n * the ephemeral-CEK happy-path zeroing elsewhere. {@link computeBrokerProof}\n * zeroes the `seed` argument in a `finally` on every path including a throw;\n * {@link deriveBrokerProofKey} zeroes its own transient HKDF-output bytes the\n * same way. {@link deriveBrokerProofBits} does not zero `seed` — it exists\n * so the party layer can register the raw bits at one-time enrol (the value\n * that must survive the call to be base64-encoded and POSTed).\n *\n * @module\n */\nimport type { EnclaveKey } from '../crypto.js'\nimport { base64ToBuffer, bufferToBase64 } from '../crypto.js'\nimport { ValidationError } from '../../errors.js'\n\nconst subtle = globalThis.crypto.subtle\n\n/** HKDF salt domain for the broker proof key — also the first `info` array element. */\nexport const BROKER_PROOF_DOMAIN = 'noydb-broker-proof'\n\n/** MAC version tag — the first element of the `canonical` array signed/verified. */\nexport const BROKER_PROOF_VERSION = 'noydb-broker-proof-v1'\n\n/** Default challenge TTL (spec §2 step 2: \"TTL ≤ 60 s\"). */\nconst DEFAULT_CHALLENGE_TTL_MS = 60_000\n\n/** The canonical-binding fields a client supplies alongside `vaultId`/`brokerId` (spec §2 step 3). */\nexport interface BrokerProofCanonicalParts {\n readonly endpointOrigin: string\n readonly profile?: string | undefined\n readonly instancePid?: string | undefined\n readonly challenge: string\n readonly expiresAt: string\n}\n\n/** A freshly-minted, server-random single-use challenge (spec §2 step 2). */\nexport interface IssuedChallenge {\n readonly challenge: string\n readonly expiresAt: string\n}\n\n/** Arguments the broker host passes to verify a submitted proof (spec §3). */\nexport interface VerifyBrokerProofArgs {\n /**\n * Atomically test-and-delete the challenge, returning whether it was still\n * fresh (unconsumed and unexpired-from-the-store's-perspective). MUST burn\n * on presentation, not on successful verification (F2).\n */\n readonly consumeChallenge: (challenge: string) => Promise<boolean>\n /** The registered proof key's raw bytes, already KMS-unwrapped by the host (§7). */\n readonly registeredProofKey: Uint8Array\n readonly vaultId: string\n readonly endpointOrigin: string\n readonly brokerId: string\n readonly profile?: string | undefined\n readonly instancePid?: string | undefined\n readonly challenge: string\n readonly expiresAt: string\n /** base64(HMAC-SHA-256(proofKey, canonical)), as submitted by the client. */\n readonly proof: string\n}\n\n/** Build the canonical MAC input verbatim — never reparses/reformats a field (F8). */\nfunction buildCanonical(vaultId: string, brokerId: string, parts: BrokerProofCanonicalParts): string {\n return JSON.stringify([\n BROKER_PROOF_VERSION,\n vaultId,\n parts.endpointOrigin,\n brokerId,\n parts.profile ?? '',\n parts.instancePid ?? '',\n parts.challenge,\n parts.expiresAt,\n ])\n}\n\n/**\n * The HKDF step: derive the 256-bit proof bits from the `_broker` seed,\n * domain-separated per `(vaultId, brokerId)`. Returns the raw bits — the\n * value registered with the broker host once at enrol (spec §2 step 2/3).\n * Does not mutate or zero `seed`; the caller owns the seed's lifecycle.\n */\nexport async function deriveBrokerProofBits(\n seed: Uint8Array,\n vaultId: string,\n brokerId: string,\n): Promise<Uint8Array> {\n const hkdfKey = await subtle.importKey('raw', seed as BufferSource, 'HKDF', false, ['deriveBits'])\n const salt = new TextEncoder().encode(BROKER_PROOF_DOMAIN)\n const info = new TextEncoder().encode(JSON.stringify([BROKER_PROOF_DOMAIN, vaultId, brokerId]))\n const bits = await subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt, info }, hkdfKey, 256)\n return new Uint8Array(bits)\n}\n\n/**\n * Derive the steady-state, non-extractable `['sign']` HMAC proof key from\n * the `_broker` seed. Zeroes the transient HKDF-output bytes in a `finally`\n * once the key has been imported (F5) — the raw bits never leave this\n * function in usable form after import.\n */\nexport async function deriveBrokerProofKey(\n seed: Uint8Array,\n vaultId: string,\n brokerId: string,\n): Promise<EnclaveKey> {\n const proofBits = await deriveBrokerProofBits(seed, vaultId, brokerId)\n try {\n return await subtle.importKey('raw', proofBits as BufferSource, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])\n } finally {\n proofBits.fill(0)\n }\n}\n\n/**\n * Compose derive + sign for the client: re-derive the proof key from the\n * seed and sign the canonical over `canonicalParts` (spec §2 step 3).\n *\n * Rejects an empty-string `instancePid` (F7 — absent is fine, `''` is not).\n * Zeroes the `seed` argument in a `finally` on EVERY path including a throw\n * (F5, durable-secret discipline) — the transient HKDF bits are zeroed one\n * layer down, inside {@link deriveBrokerProofKey}.\n */\nexport async function computeBrokerProof(\n seed: Uint8Array,\n vaultId: string,\n brokerId: string,\n canonicalParts: BrokerProofCanonicalParts,\n): Promise<string> {\n try {\n if (canonicalParts.instancePid === '') {\n throw new ValidationError(\n \"computeBrokerProof: instancePid must not be an empty string — omit the field entirely for \" +\n \"'no instance' (F7); '' would alias onto a registered instance.\",\n )\n }\n const canonical = buildCanonical(vaultId, brokerId, canonicalParts)\n const key = await deriveBrokerProofKey(seed, vaultId, brokerId)\n const mac = await subtle.sign('HMAC', key, new TextEncoder().encode(canonical) as BufferSource)\n return bufferToBase64(new Uint8Array(mac))\n } finally {\n seed.fill(0)\n }\n}\n\n/**\n * Host-side nonce mint: 32 random bytes, base64-encoded, plus an ISO\n * `expiresAt` at most `ttlMs` (default 60 s) in the future. The caller\n * (the broker host) persists both returned strings **verbatim** and MACs\n * those exact bytes on verify — never a reparsed/reformatted copy (F8).\n */\nexport function issueChallenge(opts?: { ttlMs?: number }): IssuedChallenge {\n const ttlMs = opts?.ttlMs ?? DEFAULT_CHALLENGE_TTL_MS\n const bytes = globalThis.crypto.getRandomValues(new Uint8Array(32))\n return {\n challenge: bufferToBase64(bytes),\n expiresAt: new Date(Date.now() + ttlMs).toISOString(),\n }\n}\n\n/**\n * Verify a submitted broker proof. Order is NON-NEGOTIABLE (spec §3):\n *\n * 1. `await consumeChallenge(challenge)` FIRST — burn-and-check-fresh; a\n * not-fresh (already-consumed or unknown) challenge short-circuits to\n * `false` before any MAC work (F2).\n * 2. Empty-string `instancePid` is rejected (F7), then the canonical is\n * recomputed over the verbatim `endpointOrigin`/`brokerId`/`profile`/\n * `instancePid`/`challenge`/`expiresAt` and compared via\n * `crypto.subtle.verify('HMAC', …)` — portable, constant-time, never a\n * manual compare or Node `timingSafeEqual` (F6).\n * 3. `now < expiresAt` — the client clock is never trusted; `expiresAt`\n * rides inside the already-verified MAC (F4/F8).\n */\nexport async function verifyBrokerProof(args: VerifyBrokerProofArgs): Promise<boolean> {\n const fresh = await args.consumeChallenge(args.challenge)\n if (!fresh) return false\n\n if (args.instancePid === '') return false\n\n const canonical = buildCanonical(args.vaultId, args.brokerId, {\n endpointOrigin: args.endpointOrigin,\n profile: args.profile,\n instancePid: args.instancePid,\n challenge: args.challenge,\n expiresAt: args.expiresAt,\n })\n const key = await subtle.importKey(\n 'raw',\n args.registeredProofKey as BufferSource,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['verify'],\n )\n const ok = await subtle.verify(\n 'HMAC',\n key,\n base64ToBuffer(args.proof) as BufferSource,\n new TextEncoder().encode(canonical) as BufferSource,\n )\n if (!ok) return false\n\n return Date.now() < Date.parse(args.expiresAt)\n}\n"],"mappings":"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","names":["plaintext","iv","data","iv","data","iv","data","subtle"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/kernel/sync-policy.ts","../src/with-sync/strategy.ts"],"sourcesContent":["/**\n * Sync scheduling policy.\n *\n * ## What it controls\n *\n * A {@link SyncPolicy} has two halves:\n * - **push** ({@link PushPolicy}) — when dirty local writes are sent to the remote.\n * - **pull** ({@link PullPolicy}) — when the remote is polled for new data.\n *\n * ## Choosing a policy\n *\n * The right policy depends on the backend's operational characteristics:\n *\n * | Backend type | Recommended policy |\n * |---|---|\n * | Per-record (DynamoDB, S3, IDB) | {@link INDEXED_STORE_POLICY} — `on-change` push, `manual` pull |\n * | Bundle (Drive, WebDAV, Git) | {@link POD_STORE_POLICY} — `debounce` push, `interval` pull |\n *\n * Consumers can override via `createNoydb({ syncPolicy: { ... } })`:\n *\n * ```ts\n * const db = await createNoydb({\n * store: toFile({ dir: './data' }),\n * syncPolicy: {\n * push: { mode: 'debounce', debounceMs: 5_000 },\n * pull: { mode: 'on-focus' },\n * },\n * })\n * ```\n *\n * ## Scheduler lifecycle\n *\n * {@link SyncScheduler} owns all timers, debounce logic, and browser lifecycle\n * hooks (`visibilitychange`, `pagehide`, `beforeExit`). Call `scheduler.start()`\n * after opening a vault and `scheduler.stop()` when closing it. The scheduler\n * delegates actual push/pull work to {@link SyncSchedulerCallbacks} provided\n * by the {@link SyncEngine}.\n *\n * @module\n */\n\n// ─── Policy types ───────────────────────────────────────────────────────\n\n/**\n * When push operations are triggered automatically.\n *\n * - `'manual'` — only on explicit `sync.push()` calls.\n * - `'on-change'` — immediately after every local write (respecting `minIntervalMs`).\n * - `'debounce'` — after `debounceMs` of inactivity following a write.\n * - `'interval'` — on a fixed timer regardless of writes.\n */\nexport type PushMode = 'manual' | 'on-change' | 'debounce' | 'interval'\n\n/**\n * When pull operations are triggered automatically.\n *\n * - `'manual'` — only on explicit `sync.pull()` calls.\n * - `'interval'` — on a fixed `intervalMs` timer.\n * - `'on-focus'` — when the browser tab regains visibility.\n * - `'phased'` — pull the collections named in `sequence`, in order, one at a\n * time, then settle into steady state. For thin clients that need their\n * navigation-critical collections before bulk history.\n */\nexport type PullMode = 'manual' | 'interval' | 'on-focus' | 'phased'\n\n/**\n * Push half of a sync policy. Controls the trigger mode and timing guards\n * for outbound sync operations.\n */\nexport interface PushPolicy {\n /** Push trigger mode. */\n readonly mode: PushMode\n /** Debounce delay in ms. Only used when `mode: 'debounce'`. Default: 30_000. */\n readonly debounceMs?: number\n /** Interval in ms between automatic pushes. Used by `'interval'` and as floor for `'debounce'`. */\n readonly intervalMs?: number\n /**\n * Hard floor between pushes regardless of mode. Prevents burst writes\n * from hammering the remote. Default: 0 (no floor).\n */\n readonly minIntervalMs?: number\n /**\n * Force a push on page unload (`pagehide` / `visibilitychange → hidden`)\n * in browsers, `beforeExit` in Node. Default: true for non-manual modes.\n */\n readonly onUnload?: boolean\n}\n\n/**\n * Pull half of a sync policy. Controls when and how often inbound sync\n * operations are triggered.\n */\nexport interface PullPolicy {\n /** Pull trigger mode. */\n readonly mode: PullMode\n /**\n * Interval in ms between automatic pulls. Used by `'interval'` mode. Default: 60_000.\n * Under `'phased'` it is the steady-state cadence adopted *after* the sequence\n * completes; omit it to go idle instead.\n */\n readonly intervalMs?: number\n /**\n * Collection names to pull in order, one at a time. Required when\n * `mode: 'phased'` and rejected otherwise. Entries must be non-empty and\n * unique — without period narrowing a repeated collection can only be a\n * mistake, so it is rejected rather than silently merged.\n *\n * Period-scoped phases (`collection@period`) are deferred to `partitions`;\n * use `db.pull(vault, { periods })` explicitly meanwhile.\n */\n readonly sequence?: readonly string[]\n}\n\n/**\n * Combined push + pull sync scheduling policy for a vault.\n *\n * Pass via `createNoydb({ syncPolicy })` to override the default policy\n * derived from the active store type. Pre-built defaults are available\n * as `INDEXED_STORE_POLICY` and `POD_STORE_POLICY`.\n */\nexport interface SyncPolicy {\n readonly push: PushPolicy\n readonly pull: PullPolicy\n}\n\n// ─── Default policies by store category ─────────────────────────────────\n\n/** Default for per-record stores (DynamoDB, S3, file, IDB). */\nexport const INDEXED_STORE_POLICY: SyncPolicy = {\n push: { mode: 'on-change', minIntervalMs: 0, onUnload: true },\n pull: { mode: 'manual' },\n}\n\n/** Default for bundle stores (Drive, WebDAV, Git). */\nexport const POD_STORE_POLICY: SyncPolicy = {\n push: { mode: 'debounce', debounceMs: 30_000, minIntervalMs: 120_000, onUnload: true },\n pull: { mode: 'interval', intervalMs: 60_000 },\n}\n\n/** @deprecated Use `POD_STORE_POLICY`. */\nexport const BUNDLE_STORE_POLICY = POD_STORE_POLICY\n\n// ─── Sync scheduler ─────────────────────────────────────────────────────\n\n/**\n * Current operational state of the `SyncScheduler`.\n *\n * - `'idle'` — no pending or active sync operations.\n * - `'pending'` — local writes are queued, waiting for debounce/interval to fire.\n * - `'pushing'` — push in progress.\n * - `'pulling'` — pull in progress.\n * - `'error'` — last sync operation failed; `lastError` holds the cause.\n */\nexport type SyncSchedulerState = 'idle' | 'pending' | 'pushing' | 'pulling' | 'error'\n\n/**\n * How much of a collection a caller may rely on, under a `'phased'` pull.\n *\n * - `'cold'` — not pulled yet, or its phase did not complete cleanly. A miss\n * from `get()` proves nothing.\n * - `'pulling'` — its phase is in flight. Never terminal: a phase always ends\n * in `'live'` or back in `'cold'`, because a stuck `'pulling'` would leave a\n * permanent skeleton in the UI.\n * - `'live'` — its phase completed cleanly, so a miss is a real absence.\n */\nexport type ReadinessState = 'cold' | 'pulling' | 'live'\n\n/**\n * What a scheduler-initiated pull achieved, as reported by the callback.\n *\n * `'incomplete'` covers both a pull that reported errors (`PullResult.errors`\n * accumulates without throwing) and one that was skipped — e.g. role-gated on a\n * backup target. Either way nothing may be claimed about completeness.\n */\nexport type PullPhaseOutcome = 'complete' | 'incomplete'\n\n/**\n * Snapshot of the sync scheduler's state, returned by `SyncScheduler.status`.\n * Safe to expose in a reactive UI status indicator.\n */\nexport interface SyncSchedulerStatus {\n readonly state: SyncSchedulerState\n readonly lastPushAt: string | null\n readonly lastPullAt: string | null\n readonly lastError: Error | null\n readonly pendingWrites: number\n /**\n * Per-collection readiness. Empty unless `pull.mode === 'phased'`.\n *\n * A collection the sequence never names is **absent** from the map, and\n * `undefined` means *\"no claim made\"* — never a reason to gate a UI.\n */\n readonly readiness: ReadonlyMap<string, ReadinessState>\n /** 1-based position in the sequence, or `null` outside a phased run. */\n readonly phase: { readonly index: number; readonly total: number } | null\n}\n\n/**\n * Callbacks injected into `SyncScheduler` by the SyncEngine.\n *\n * The scheduler owns timers and lifecycle hooks; it delegates actual push/pull\n * work to these callbacks to stay decoupled from the sync implementation.\n */\nexport interface SyncSchedulerCallbacks {\n push(): Promise<void>\n /**\n * @param collections - Narrow the pull to these collections. Passed by a\n * `'phased'` sequence, one collection per phase; omitted for a whole-vault pull.\n * @returns Whether the pull may be treated as complete. Returning `void` reads\n * as `'complete'`, so callers with nothing to report need not opt in.\n */\n pull(collections?: readonly string[]): Promise<PullPhaseOutcome | void>\n getDirtyCount(): number\n}\n\n/**\n * Reject a policy that cannot mean anything, at construction rather than on\n * the first tick. Module-private: `SyncScheduler` is the choke point every\n * policy that can actually schedule must pass through — primary and per-target\n * alike — so validating here needs no new public surface and costs the kernel\n * floor nothing.\n */\nfunction validatePullPolicy(pull: PullPolicy): void {\n if (pull.mode !== 'phased') {\n if (pull.sequence !== undefined) {\n throw new Error(\n `syncPolicy.pull.sequence is only meaningful with mode: 'phased' (got '${pull.mode}'). ` +\n `Set mode: 'phased' to run the sequence, or drop the field.`,\n )\n }\n return\n }\n\n const { sequence } = pull\n if (!sequence || sequence.length === 0) {\n throw new Error(\n `syncPolicy.pull.mode: 'phased' requires a non-empty 'sequence' of collection names.`,\n )\n }\n for (const name of sequence) {\n if (typeof name !== 'string' || name.length === 0) {\n throw new Error(\n `syncPolicy.pull.sequence entries must be non-empty collection names (got ${JSON.stringify(name)}).`,\n )\n }\n }\n const duplicate = sequence.find((name, i) => sequence.indexOf(name) !== i)\n if (duplicate !== undefined) {\n throw new Error(\n `syncPolicy.pull.sequence lists \"${duplicate}\" more than once. ` +\n `Each collection is pulled whole, so a repeat does no additional work.`,\n )\n }\n}\n\n/**\n * Manages sync timing according to a `SyncPolicy`.\n *\n * The scheduler owns all timers and lifecycle hooks. It delegates actual\n * push/pull work to callbacks provided by the SyncEngine.\n */\nexport class SyncScheduler {\n private readonly policy: SyncPolicy\n private readonly callbacks: SyncSchedulerCallbacks\n\n private _state: SyncSchedulerState = 'idle'\n private _lastPushAt: string | null = null\n private _lastPullAt: string | null = null\n private _lastError: Error | null = null\n private _lastPushTime = 0 // monotonic ms for minIntervalMs enforcement\n\n // Phased-pull progress. Empty for every other mode, so non-adopters carry\n // an empty Map and a null — no per-read cost, nothing to interpret.\n private readonly _readiness = new Map<string, ReadinessState>()\n private _phase: { readonly index: number; readonly total: number } | null = null\n\n // Timers\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\n private pushIntervalTimer: ReturnType<typeof setInterval> | null = null\n private pullIntervalTimer: ReturnType<typeof setInterval> | null = null\n\n // Bound handlers for cleanup\n private readonly boundOnVisibilityChange: (() => void) | null = null\n private readonly boundOnBeforeExit: (() => void) | null = null\n private readonly boundOnPageHide: (() => void) | null = null\n\n private started = false\n\n constructor(policy: SyncPolicy, callbacks: SyncSchedulerCallbacks) {\n validatePullPolicy(policy.pull)\n this.policy = policy\n this.callbacks = callbacks\n\n // Pre-bind handlers\n if (this.shouldRegisterUnload()) {\n this.boundOnVisibilityChange = this.handleVisibilityChange.bind(this)\n this.boundOnPageHide = this.handlePageHide.bind(this)\n this.boundOnBeforeExit = this.handleBeforeExit.bind(this)\n }\n }\n\n /** Current scheduler status snapshot. */\n get status(): SyncSchedulerStatus {\n return {\n state: this._state,\n lastPushAt: this._lastPushAt,\n lastPullAt: this._lastPullAt,\n lastError: this._lastError,\n pendingWrites: this.callbacks.getDirtyCount(),\n // Copied, not aliased — a status snapshot must not mutate under its reader.\n readiness: new Map(this._readiness),\n phase: this._phase,\n }\n }\n\n /** Start the scheduler — registers timers, event listeners. */\n start(): void {\n if (this.started) return\n this.started = true\n\n // Push: interval mode\n if (this.policy.push.mode === 'interval' && this.policy.push.intervalMs) {\n this.pushIntervalTimer = setInterval(() => {\n void this.executePush()\n }, this.policy.push.intervalMs)\n }\n\n // Pull: interval mode\n if (this.policy.pull.mode === 'interval') {\n this.startPullInterval()\n }\n\n // Pull: phased mode — walk the sequence in order, then settle into steady\n // state. Unawaited by design: start() is synchronous and the caller\n // (vault open) must not block on a bootstrap pull.\n if (this.policy.pull.mode === 'phased') {\n const sequence = this.policy.pull.sequence ?? []\n // Every named collection is 'cold' from the moment we start, so a reader\n // that races the first phase sees \"not ready\", never a missing entry.\n for (const collection of sequence) this._readiness.set(collection, 'cold')\n void this.runSequence(sequence)\n }\n\n // Pull: on-focus mode\n if (this.policy.pull.mode === 'on-focus' && typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', this.handleFocusPull)\n }\n\n // Unload hooks\n if (this.shouldRegisterUnload()) {\n if (typeof document !== 'undefined' && this.boundOnVisibilityChange) {\n document.addEventListener('visibilitychange', this.boundOnVisibilityChange)\n }\n if (typeof globalThis.addEventListener === 'function' && this.boundOnPageHide) {\n globalThis.addEventListener('pagehide', this.boundOnPageHide)\n }\n if (typeof process !== 'undefined' && this.boundOnBeforeExit) {\n process.on('beforeExit', this.boundOnBeforeExit)\n }\n }\n }\n\n /** Stop the scheduler — clears timers, removes event listeners. */\n stop(): void {\n if (!this.started) return\n this.started = false\n\n // A sequence cut mid-phase must not leave that collection 'pulling' —\n // a reader would show a skeleton that never resolves.\n for (const [collection, state] of this._readiness) {\n if (state === 'pulling') this._readiness.set(collection, 'cold')\n }\n this._phase = null\n\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer)\n this.debounceTimer = null\n }\n if (this.pushIntervalTimer) {\n clearInterval(this.pushIntervalTimer)\n this.pushIntervalTimer = null\n }\n if (this.pullIntervalTimer) {\n clearInterval(this.pullIntervalTimer)\n this.pullIntervalTimer = null\n }\n\n // Focus pull\n if (this.policy.pull.mode === 'on-focus' && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.handleFocusPull)\n }\n\n // Unload hooks\n if (typeof document !== 'undefined' && this.boundOnVisibilityChange) {\n document.removeEventListener('visibilitychange', this.boundOnVisibilityChange)\n }\n if (typeof globalThis.removeEventListener === 'function' && this.boundOnPageHide) {\n globalThis.removeEventListener('pagehide', this.boundOnPageHide)\n }\n if (typeof process !== 'undefined' && this.boundOnBeforeExit) {\n process.removeListener('beforeExit', this.boundOnBeforeExit)\n }\n }\n\n /**\n * Notify the scheduler that a local write occurred.\n * For `on-change` mode: triggers immediate push (respecting minIntervalMs).\n * For `debounce` mode: resets the debounce timer.\n * For `manual` / `interval`: no-op.\n */\n notifyChange(): void {\n if (!this.started) return\n\n if (this.policy.push.mode === 'on-change') {\n void this.executePush()\n } else if (this.policy.push.mode === 'debounce') {\n this.resetDebounce()\n }\n }\n\n /** Force an immediate push, bypassing the scheduler. */\n async forcePush(): Promise<void> {\n await this.executePush()\n }\n\n /** Force an immediate pull, bypassing the scheduler. */\n async forcePull(): Promise<void> {\n await this.executePull()\n }\n\n // ─── Internal ─────────────────────────────────────────────────────\n\n /**\n * Arm the steady-state pull timer, if the policy specifies a cadence.\n * Shared by `'interval'` mode and by `'phased'` once its sequence drains.\n */\n private startPullInterval(): void {\n if (this.pullIntervalTimer || !this.policy.pull.intervalMs) return\n this.pullIntervalTimer = setInterval(() => {\n void this.executePull()\n }, this.policy.pull.intervalMs)\n }\n\n /**\n * Pull each collection in turn, then hand over to steady state.\n *\n * Strictly sequential — running phases concurrently would defeat the\n * prioritisation that is the entire point of a sequence. Each phase is an\n * ordinary scoped pull, so a phase that reports errors does not abort the\n * ones behind it; `executePull` records the failure and the walk continues.\n * `started` is re-checked between phases so `stop()` cuts a sequence short\n * instead of letting it run on against a closed vault.\n */\n private async runSequence(sequence: readonly string[]): Promise<void> {\n for (const [i, collection] of sequence.entries()) {\n if (!this.started) return\n this._readiness.set(collection, 'pulling')\n this._phase = { index: i + 1, total: sequence.length }\n\n const outcome = await this.executePull([collection])\n\n // 'live' only on a clean phase. Anything else — reported errors, a\n // role-gated skip, a throw — goes back to 'cold', because 'live' is a\n // promise that a miss from get() is a real absence.\n this._readiness.set(collection, outcome === 'complete' ? 'live' : 'cold')\n }\n this._phase = null\n if (this.started) this.startPullInterval()\n }\n\n private async executePush(): Promise<void> {\n if (this._state === 'pushing') return // already in progress\n\n // minIntervalMs enforcement\n const minInterval = this.policy.push.minIntervalMs ?? 0\n if (minInterval > 0) {\n const elapsed = Date.now() - this._lastPushTime\n if (elapsed < minInterval) {\n // Schedule for later if debounce mode\n if (this.policy.push.mode === 'debounce') {\n this.scheduleDebounce(minInterval - elapsed)\n }\n return\n }\n }\n\n // Nothing to push\n if (this.callbacks.getDirtyCount() === 0) {\n this._state = 'idle'\n return\n }\n\n this._state = 'pushing'\n try {\n await this.callbacks.push()\n this._lastPushAt = new Date().toISOString()\n this._lastPushTime = Date.now()\n this._lastError = null\n this._state = this.callbacks.getDirtyCount() > 0 ? 'pending' : 'idle'\n } catch (err) {\n this._lastError = err instanceof Error ? err : new Error(String(err))\n this._state = 'error'\n }\n }\n\n private async executePull(collections?: readonly string[]): Promise<PullPhaseOutcome> {\n if (this._state === 'pulling') return 'incomplete'\n\n const previousState = this._state\n this._state = 'pulling'\n try {\n const outcome = await this.callbacks.pull(collections)\n this._lastPullAt = new Date().toISOString()\n this._lastError = null\n this._state = previousState === 'pending' ? 'pending' : 'idle'\n return outcome ?? 'complete'\n } catch (err) {\n this._lastError = err instanceof Error ? err : new Error(String(err))\n this._state = 'error'\n return 'incomplete'\n }\n }\n\n private resetDebounce(): void {\n if (this.debounceTimer) clearTimeout(this.debounceTimer)\n const ms = this.policy.push.debounceMs ?? 30_000\n this._state = 'pending'\n this.scheduleDebounce(ms)\n }\n\n private scheduleDebounce(ms: number): void {\n if (this.debounceTimer) clearTimeout(this.debounceTimer)\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null\n void this.executePush()\n }, ms)\n }\n\n private shouldRegisterUnload(): boolean {\n const onUnload = this.policy.push.onUnload\n if (onUnload !== undefined) return onUnload\n return this.policy.push.mode !== 'manual'\n }\n\n // ─── Event handlers ───────────────────────────────────────────────\n\n private handleVisibilityChange(): void {\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {\n this.fireUnloadPush()\n }\n }\n\n private handlePageHide(): void {\n this.fireUnloadPush()\n }\n\n private handleBeforeExit(): void {\n this.fireUnloadPush()\n }\n\n private handleFocusPull = (): void => {\n if (typeof document !== 'undefined' && document.visibilityState === 'visible') {\n void this.executePull()\n }\n }\n\n private fireUnloadPush(): void {\n if (this.callbacks.getDirtyCount() === 0) return\n // Best-effort synchronous-ish push on unload\n void this.callbacks.push().catch(() => {})\n }\n}\n","/**\n * Strategy seam for the optional sync engine + presence service.\n * Core imports `SyncStrategy` type-only + `NO_SYNC` stub; the real\n * `SyncEngine`, `SyncTransaction`, and `PresenceHandle` constructors\n * are only reachable via `withSync()` in `./sync-active.ts`.\n *\n * Solo apps that never configure `sync` and never call\n * `collection.presence()` ship none of the ~856 LOC behind this seam\n * (`sync.ts` + `sync-transaction.ts` + `presence.ts`).\n *\n * Note: `keyring.ts` (~746 LOC) stays in core because it's required\n * for any multi-user vault — even single-owner vaults use a keyring\n * to wrap the DEK. The team package's grant/revoke/magic-link/\n * delegation modules tree-shake naturally via direct named imports.\n *\n * Behavior under NO_SYNC:\n *\n * - **buildSyncEngine** — throws. Only fires when `createNoydb({ sync })`\n * passes a remote target.\n * - **buildSyncTransaction** — throws. Only fires when `db.transaction(vault)`\n * is called on a vault with sync configured.\n * - **buildPresence** — throws. Only fires when user code calls\n * `collection.presence()`.\n *\n * @internal\n */\n\nimport type {\n NoydbStore,\n ConflictStrategy,\n SyncTargetRole,\n} from '../kernel/types.js'\nimport type { NoydbEventEmitter } from '../kernel/events.js'\nimport type { SyncPolicy } from '../kernel/sync-policy.js'\nimport type { SyncEngine } from './engine.js'\nimport type { SyncTransaction } from './transaction.js'\nimport type { PresenceHandle, PresenceHandleOpts } from './presence.js'\nimport type { Vault } from '../kernel/vault.js'\n\n/**\n * Options accepted by `SyncStrategy.buildSyncEngine`. Mirrors the\n * `SyncEngine` constructor verbatim — kept here so core code never\n * imports the sync module at runtime.\n *\n * @internal\n */\nexport interface BuildSyncEngineOptions {\n local: NoydbStore\n remote: NoydbStore\n vault: string\n strategy: ConflictStrategy\n emitter: NoydbEventEmitter\n syncPolicy?: SyncPolicy\n role?: SyncTargetRole\n label?: string\n}\n\n/**\n * @internal\n */\nexport interface SyncStrategy {\n buildSyncEngine(opts: BuildSyncEngineOptions): SyncEngine\n buildSyncTransaction(vault: Vault, engine: SyncEngine): SyncTransaction\n buildPresence<P>(opts: PresenceHandleOpts): PresenceHandle<P>\n}\n\nfunction notEnabled(op: string): Error {\n return new Error(\n `${op} requires the sync strategy. Import ` +\n '`{ withSync }` from \"@noy-db/hub/sync\" and pass it to ' +\n '`createNoydb({ syncStrategy: withSync() })`.',\n )\n}\n\n/**\n * No-sync stub. Every constructor throws with an actionable pointer\n * — there is no useful \"off\" mode for sync engine / presence /\n * sync-transaction; if the consumer reached one of these surfaces,\n * they intended to use it.\n *\n * @internal\n */\nexport const NO_SYNC: SyncStrategy = {\n buildSyncEngine() { throw notEnabled('SyncEngine') },\n buildSyncTransaction() { throw notEnabled('SyncTransaction') },\n buildPresence() { throw notEnabled('collection.presence()') },\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-pod/pod-store.ts","../src/with-pod/redirect.ts","../src/with-pod/open.ts"],"sourcesContent":["import type { NoydbStore, NoydbPodStore, VaultSnapshot, EncryptedEnvelope } from '../kernel/types.js'\nimport { ConflictError, PodVersionConflictError } from '../kernel/errors.js'\n\n// ─── Bundle format ─────────────────────────────────────────────────────\n\nconst BUNDLE_STORE_VERSION = 1 as const\n\n/**\n * Wire format written by `wrapPodStore`. A JSON-serialised object that\n * contains the entire `VaultSnapshot` (all encrypted envelopes) plus a small\n * header for integrity checking. The envelopes inside are already AES-GCM\n * encrypted by core — the bundle bytes themselves are not additionally\n * encrypted, but they are safe to store on untrusted blob hosts because\n * every record inside is already ciphertext.\n *\n * @internal\n */\ninterface BundleStoreData {\n readonly _noydb_bundle_store: typeof BUNDLE_STORE_VERSION\n readonly vault: string\n readonly ts: string\n readonly data: VaultSnapshot\n}\n\n// ─── Options ───────────────────────────────────────────────────────────\n\nexport interface WrapPodStoreOptions {\n /**\n * When `true` (default), every `put()` and `delete()` flushes the full\n * vault snapshot to the bundle backend. Set to `false` for bulk operations\n * and call `store.flush(vaultId)` manually.\n */\n autoFlush?: boolean\n}\n\n// ─── Extended NoydbStore with flush/batch ───────────────────────────────\n\nexport interface WrappedPodNoydbStore extends NoydbStore {\n /** Manually flush the in-memory snapshot to the bundle backend. */\n flush(vaultId: string): Promise<void>\n /**\n * Run a batch of mutations without flushing until the callback completes.\n * A single flush is performed at the end.\n */\n batch(vaultId: string, fn: () => Promise<void>): Promise<void>\n}\n\n// ─── wrapPodStore ───────────────────────────────────────────────────\n\nconst MAX_CONFLICT_RETRIES = 3\n\n/**\n * Convert a `NoydbPodStore` (blob-oriented read/write with OCC) into the\n * standard six-method `NoydbStore` interface expected by `createNoydb({ store })`.\n *\n * Bundle stores operate on the entire vault as a single serialised unit —\n * ideal for backends like Google Drive, WebDAV, or iCloud Drive that work\n * best with whole-file I/O rather than per-record KV operations.\n *\n * ## Optimistic concurrency\n *\n * The wrapper tracks the `version` token from the last `readBundle` and\n * passes it as `expectedVersion` on every flush. On\n * `PodVersionConflictError`, it re-reads, merges the remote snapshot\n * (last-write-wins per record key), and retries (max 3 attempts).\n *\n * ## Flush modes\n *\n * By default, flushes on every mutation (O(vault size) per write). Options:\n * - `autoFlush: false` + explicit `store.flush(vaultId)` calls\n * - `store.batch(vaultId, async () => { ... })` — defers flush until end\n * - Pair with `syncPolicy: { push: { mode: 'debounce' } }` from \n */\nexport function wrapPodStore(\n bundle: NoydbPodStore,\n options?: WrapPodStoreOptions,\n): WrappedPodNoydbStore {\n const autoFlush = options?.autoFlush !== false\n\n // Per-vault state\n const snapshots = new Map<string, VaultSnapshot>()\n const versions = new Map<string, string | null>()\n const loaded = new Set<string>()\n\n // Batch mode: when > 0, suppress auto-flush\n let batchDepth = 0\n\n // #908 — in-flight loads, keyed by vault. Without this, every concurrent\n // caller issued its own `readBundle` and then REPLACED `snapshots` with a\n // freshly parsed object — orphaning the mutations earlier callers had\n // already made to the object they were handed. 100 racing puts kept 1.\n const loading = new Map<string, Promise<VaultSnapshot>>()\n\n async function load(vault: string): Promise<VaultSnapshot> {\n if (loaded.has(vault)) return snapshots.get(vault)!\n\n const inFlight = loading.get(vault)\n if (inFlight) return inFlight\n\n const pending = (async () => {\n const result = await bundle.readBundle(vault)\n if (result) {\n const text = new TextDecoder().decode(result.bytes)\n const format = JSON.parse(text) as BundleStoreData\n snapshots.set(vault, format.data)\n versions.set(vault, result.version)\n } else {\n snapshots.set(vault, {})\n versions.set(vault, null)\n }\n\n loaded.add(vault)\n return snapshots.get(vault)!\n })()\n\n loading.set(vault, pending)\n try {\n return await pending\n } finally {\n // Clear on failure too, so a transient read error does not poison\n // every later load of this vault with a rejected promise.\n loading.delete(vault)\n }\n }\n\n async function flush(vault: string): Promise<void> {\n const snapshot = snapshots.get(vault) ?? {}\n const format: BundleStoreData = {\n _noydb_bundle_store: BUNDLE_STORE_VERSION,\n vault,\n ts: new Date().toISOString(),\n data: snapshot,\n }\n const bytes = new TextEncoder().encode(JSON.stringify(format))\n const expectedVersion = versions.get(vault) ?? null\n\n for (let attempt = 0; attempt < MAX_CONFLICT_RETRIES; attempt++) {\n try {\n const { version: newVersion } = await bundle.writeBundle(vault, bytes, expectedVersion)\n versions.set(vault, newVersion)\n return\n } catch (err) {\n if (err instanceof PodVersionConflictError && attempt < MAX_CONFLICT_RETRIES - 1) {\n // Pull remote, merge (last-write-wins by record key), retry\n const remote = await bundle.readBundle(vault)\n if (remote) {\n const remoteText = new TextDecoder().decode(remote.bytes)\n const remoteFormat = JSON.parse(remoteText) as BundleStoreData\n const localSnap = snapshots.get(vault) ?? {}\n const mergedSnap = mergeSnapshots(remoteFormat.data, localSnap)\n snapshots.set(vault, mergedSnap)\n versions.set(vault, remote.version)\n }\n // Re-encode with merged data for the retry\n continue\n }\n throw err\n }\n }\n }\n\n // #908 — one flush at a time per vault. `flush` reads and writes the vault's\n // version token, so concurrent flushes race: each sends the same\n // `expectedVersion`, the losers take the conflict/merge path, and with enough\n // of them the retry budget is exhausted and the error surfaces to a caller\n // whose write was perfectly valid. Serialising makes every flush see the\n // token its predecessor produced.\n const flushChain = new Map<string, Promise<void>>()\n\n function serialFlush(vault: string): Promise<void> {\n const prev = flushChain.get(vault) ?? Promise.resolve()\n // Chain past a rejection as well — one failed flush must not wedge every\n // subsequent write to this vault.\n const next = prev.then(() => flush(vault), () => flush(vault))\n flushChain.set(vault, next)\n return next\n }\n\n async function maybeFlush(vault: string): Promise<void> {\n if (autoFlush && batchDepth === 0) {\n await serialFlush(vault)\n }\n }\n\n const store: WrappedPodNoydbStore = {\n name: bundle.name ?? 'bundle',\n\n async flush(vaultId: string): Promise<void> {\n await serialFlush(vaultId)\n },\n\n async batch(vaultId: string, fn: () => Promise<void>): Promise<void> {\n await load(vaultId) // ensure loaded before batch\n batchDepth++\n try {\n await fn()\n } finally {\n batchDepth--\n }\n await serialFlush(vaultId)\n },\n\n async get(vault: string, collection: string, id: string): Promise<EncryptedEnvelope | null> {\n const snap = await load(vault)\n return snap[collection]?.[id] ?? null\n },\n\n async put(\n vault: string,\n collection: string,\n id: string,\n envelope: EncryptedEnvelope,\n expectedVersion?: number,\n ): Promise<void> {\n const snap = await load(vault)\n\n if (expectedVersion !== undefined) {\n const current = snap[collection]?.[id]\n const currentVersion = current?._v ?? 0\n if (currentVersion !== expectedVersion) {\n throw new ConflictError(\n currentVersion,\n `Expected version ${expectedVersion} but found ${currentVersion} on ${collection}/${id}`,\n )\n }\n }\n\n snap[collection] ??= {}\n snap[collection][id] = envelope\n await maybeFlush(vault)\n },\n\n async delete(vault: string, collection: string, id: string): Promise<void> {\n const snap = await load(vault)\n if (snap[collection]) {\n delete snap[collection][id]\n await maybeFlush(vault)\n }\n },\n\n async list(vault: string, collection: string): Promise<string[]> {\n const snap = await load(vault)\n return Object.keys(snap[collection] ?? {})\n },\n\n async loadAll(vault: string): Promise<VaultSnapshot> {\n const snap = await load(vault)\n // #908 — two contract fixes in one place:\n // 1. internal collections (`_keyring`, `_sync`) are the vault's own\n // bookkeeping and must not appear in a snapshot — `@noy-db/to-file`\n // sets the reference behaviour with this same `_`-prefix rule;\n // 2. copy rather than hand out the live cache, which let a caller\n // mutating the result silently rewrite the wrapper's own state.\n // `get()`/`list()` still serve internal collections; this is about what\n // a *snapshot* claims, not about hiding data.\n const out: VaultSnapshot = {}\n for (const [collection, records] of Object.entries(snap)) {\n if (collection.startsWith('_')) continue\n out[collection] = { ...records }\n }\n return out\n },\n\n async saveAll(vault: string, data: VaultSnapshot): Promise<void> {\n snapshots.set(vault, data)\n loaded.add(vault)\n await serialFlush(vault)\n },\n }\n\n return store\n}\n\n// ─── Snapshot merge (last-write-wins per record) ────────────────────────\n\nfunction mergeSnapshots(remote: VaultSnapshot, local: VaultSnapshot): VaultSnapshot {\n const merged: VaultSnapshot = {}\n\n // Start with all remote collections\n for (const [coll, records] of Object.entries(remote)) {\n merged[coll] = { ...records }\n }\n\n // Overlay local collections — LWW by _ts per record\n for (const [coll, records] of Object.entries(local)) {\n if (!merged[coll]) {\n merged[coll] = { ...records }\n continue\n }\n for (const [id, envelope] of Object.entries(records)) {\n const existing = merged[coll][id]\n if (!existing || envelope._ts >= existing._ts) {\n merged[coll][id] = envelope\n }\n }\n }\n\n return merged\n}\n\n// ─── Factory helper ─────────────────────────────────────────────────────\n\n/**\n * Type-safe factory helper for `NoydbPodStore` implementations,\n * analogous to `createStore` for KV stores.\n */\nexport function createPodStore<TOptions>(\n factory: (options: TOptions) => NoydbPodStore,\n): (options: TOptions) => NoydbPodStore {\n return factory\n}\n\n// ─── Deprecated aliases (pre-rename compatibility) ──────────────────────\n\n/** @deprecated Use wrapPodStore. */\nexport const wrapBundleStore = wrapPodStore\n/** @deprecated Use createPodStore. */\nexport const createBundleStore = createPodStore\n/** @deprecated Use WrappedPodNoydbStore. */\nexport type WrappedBundleNoydbStore = WrappedPodNoydbStore\n/** @deprecated Use WrapPodStoreOptions. */\nexport type WrapBundleStoreOptions = WrapPodStoreOptions\n","/**\n * Redirect record (#944) — a signed \"this moved, go there\" pointer carried\n * in the pod's PLAINTEXT header (`NoydbPodHeader.redirect`, see\n * `format.ts`) so a dispatcher can follow it pre-auth: no secret, no\n * decompression.\n *\n * The record carries its OWN inner `sig` (via `signRecord`/`verifyRecord`\n * from `signature.ts`) — independent from, and in addition to, the #943\n * pod-header signature. A Redirect is REQUIRED to be signed: there is no\n * legacy unsigned install base for this new record type, so an absent or\n * invalid signature is invalid for following, not merely \"unverified\".\n *\n * Minimum-disclosure: the record is kept to exactly\n * `{ v, target, reason, issuedBy, sig }` — no timestamps, no identities\n * beyond the signer's `keyId` fingerprint.\n */\n\nimport type { DocSigner } from '../with-audit/attestation/signer.js'\nimport { signRecord, verifyRecord } from './signature.js'\nimport { readPodRedirect } from './bundle.js'\nimport {\n RedirectBadSignatureError,\n RedirectDepthExceededError,\n RedirectLoopError,\n RedirectUnreachableError,\n} from '../kernel/errors.js'\n\n/**\n * A signed pointer to another pod. `target` is a locator string or URL —\n * the forthcoming #945 `Locator` type isn't built yet, so `target` stays a\n * plain string for now (forward-seam noted, not blurred: a Redirect says\n * \"go elsewhere once\"; a Locator will say \"where cargo lives\").\n */\nexport interface Redirect {\n readonly v: 1\n readonly target: string\n readonly reason: 'moved' | 'release' | 'tombstone' | 'repoint'\n readonly issuedBy: string\n readonly sig: string\n}\n\n/**\n * Sign a new Redirect record with `signer`. Builds the record minus `sig`,\n * signs it via the #943 `signRecord` convention, and returns the complete\n * signed record.\n */\nexport async function signRedirect(\n signer: DocSigner,\n fields: { readonly target: string; readonly reason: Redirect['reason'] },\n): Promise<Redirect> {\n const record = {\n v: 1 as const,\n target: fields.target,\n reason: fields.reason,\n issuedBy: signer.keyId,\n }\n const sig = await signRecord(signer.privateKeyPkcs8B64, record)\n return { ...record, sig }\n}\n\n/**\n * Verify a Redirect record's signature against `trustedKeys`\n * (`keyId → publicKeyB64`). Fails closed: an `issuedBy` not present in\n * `trustedKeys` is untrusted/unverifiable and returns `false`, same as a\n * tampered or forged signature.\n */\nexport async function verifyRedirect(\n record: Redirect,\n trustedKeys: Readonly<Record<string, string>>,\n): Promise<boolean> {\n const publicKeyB64 = trustedKeys[record.issuedBy]\n if (publicKeyB64 === undefined) return false\n const payload = { v: record.v, target: record.target, reason: record.reason, issuedBy: record.issuedBy }\n return verifyRecord(publicKeyB64, record.sig, payload)\n}\n\n/** One followed hop's provenance — surfaced so a UI can show \"moved from X via Y\". */\nexport interface RedirectHop {\n readonly target: string\n readonly reason: Redirect['reason']\n readonly issuedBy: string\n}\n\n/** Result of {@link followRedirects}: the terminal (non-redirect) pod's bytes, plus the ordered hop list that led there. */\nexport interface FollowRedirectsResult {\n readonly terminal: Uint8Array\n readonly hops: readonly RedirectHop[]\n}\n\n/**\n * Follow a chain of Redirect records starting from `start`'s pod bytes,\n * fetching each hop's target via `fetcher`, until a pod with no `redirect`\n * header field (the terminal) is reached.\n *\n * HTTP-redirect discipline: each hop's Redirect is verified against\n * `opts.trustedKeys` BEFORE it is followed (fail closed — an untrusted or\n * forged hop never advances the walk), loops are detected on the target\n * about to be followed, and the hop count is capped at `opts.maxDepth`\n * (default 8). `fetcher` throwing or returning `null` for a target means\n * that hop is unreachable.\n *\n * @throws {RedirectBadSignatureError} a hop's Redirect fails verification.\n * @throws {RedirectLoopError} a hop's target was already followed in this chain.\n * @throws {RedirectDepthExceededError} more than `maxDepth` hops were followed.\n * @throws {RedirectUnreachableError} `fetcher` threw or returned `null` for a target.\n */\nexport async function followRedirects(\n start: Uint8Array,\n fetcher: (target: string) => Promise<Uint8Array | null>,\n opts: { readonly trustedKeys: Readonly<Record<string, string>>; readonly maxDepth?: number },\n): Promise<FollowRedirectsResult> {\n const maxDepth = opts.maxDepth ?? 8\n let current = start\n const hops: RedirectHop[] = []\n const visited = new Set<string>()\n\n for (;;) {\n const rec = readPodRedirect(current)\n if (rec === undefined) return { terminal: current, hops }\n\n if (!(await verifyRedirect(rec, opts.trustedKeys))) {\n throw new RedirectBadSignatureError(rec.target)\n }\n if (visited.has(rec.target)) {\n throw new RedirectLoopError(rec.target)\n }\n visited.add(rec.target)\n\n hops.push({ target: rec.target, reason: rec.reason, issuedBy: rec.issuedBy })\n if (hops.length > maxDepth) {\n throw new RedirectDepthExceededError(maxDepth)\n }\n\n let next: Uint8Array | null\n try {\n next = await fetcher(rec.target)\n } catch (cause) {\n throw new RedirectUnreachableError(rec.target, cause)\n }\n if (next === null) {\n throw new RedirectUnreachableError(rec.target)\n }\n current = next\n }\n}\n","/**\n * `open(podBytesOrFile)` — the pod READ-path orchestrator (#941 Task 4).\n *\n * A free function (never a `Vault` method — `kernel/vault.ts` is at its\n * line ceiling and open() needs no vault-internal state that isn't already\n * reachable through published seams). Composes, in order:\n *\n * 1. `readPod` — parse + integrity-check the container (throws\n * `BundleIntegrityError` on a corrupted/truncated pod).\n * 2. `verifyPodHeader` — only when `opts.trustedKeys` is supplied.\n * `'unsigned'` is benign (legacy pod, or `writePod({ sign: false })`)\n * and open() proceeds; `'untrusted'` / `'tampered'` are fail-closed —\n * `PodHeaderVerificationError` — mirroring `followRedirects`'s\n * `RedirectBadSignatureError` posture: once a caller opts into\n * verification, an unverifiable header is a hard stop.\n * 3. `createNoydb` + `openVault` + `vault.load(dumpJson)` — the exact\n * unlock+restore sequence `bundle-roundtrip.test.ts` drives by hand.\n * The pod header carries no vault name (minimum-disclosure —\n * `format.ts`'s forbidden-fields list), so the caller names the\n * target vault via `opts.vault`.\n * 4. Re-derive the `SchemaManifest` from the just-restored `_schemas/*`\n * (the source of truth — Task 3's `deriveSchemaManifest`), via a\n * NON-MINTING `LookupDEK` built from `loadKeyring`'s already-unwrapped\n * `UnlockedKeyring.deks` map directly (`with-party/team/keyring.js`).\n * **Not** `ensureCollectionDEK` (#941 review fix — Important 2): that\n * helper mints+persists a fresh DEK into `_keyring/<user>` for any\n * collection absent from the keyring, which is correct for a WRITE\n * path but wrong here — `deriveSchemaManifest` only ever reads\n * collections that already have a `_schemas` envelope, so an absent\n * DEK means \"this principal can't decrypt this sibling\" (e.g. a\n * collection-scoped grantee), never \"new collection.\" The old\n * minting call polluted a scoped principal's freshly-restored keyring\n * with garbage sibling DEKs on every `open()`. The returned\n * `manifest` is that principal's own visible slice — see\n * `derive.ts`'s `LookupDEK` doc and `sync.ts`'s module doc for the\n * full rationale (the same distinction governs why the sync path\n * must SKIP rather than write a partial manifest).\n * 5. Generation check (AC #4 coexistence): compare the HIGHEST\n * per-collection generation stamp carried by the restored manifest\n * against THIS store's schema-fence generation as it stood\n * immediately before the restore (0 for a brand-new vault; whatever a\n * coexisting local vault already had). If the pod is ahead and\n * `!opts.allowGenerationAhead`, throw `MigrationRequiredError` (the\n * same class `SchemaFenceController`'s write-path gate throws) — the\n * reader hasn't reconciled schema changes that happened elsewhere.\n * `allowGenerationAhead: true` is the documented dev override: open\n * proceeds, with a console warning. Divergence in the OTHER direction\n * (reader ahead of the pod) is non-fatal but also warned (#941\n * review, Important 3 — AC #4 coexistence divergence must be\n * observable in either direction, not just the bypass case).\n *\n * NOTE: this deliberately reads per-collection entries, not\n * `manifest.generation` itself. `_meta/schema-fence` is local\n * session-coordination state and does NOT travel in a pod dump\n * (`with-pod/backup.ts`'s reserved-collection list carries `_schemas`\n * and `_manifest` but not `_meta`) — a fresh re-derive's top-level\n * `generation` is always driven by the (untouched-by-restore) target\n * store's own fence, not the pod's. Each `_schemas/<collection>`\n * envelope's own `generation` field DOES travel (it's stamped at\n * declare-time from the writer's real fence), so per-collection\n * entries are the only signal that survives a restore intact.\n *\n * @module\n */\n\nimport { readPod, verifyPodHeader } from './bundle.js'\nimport type { NoydbPodHeader } from './format.js'\nimport type { PodVerifyResult } from './bundle.js'\nimport { createNoydb } from '../kernel/noydb.js'\nimport type { Noydb } from '../kernel/noydb.js'\nimport type { Vault } from '../kernel/vault.js'\nimport type { NoydbOptions, NoydbStore, EchoSecretParts } from '../kernel/types.js'\nimport { MigrationRequiredError, PodHeaderVerificationError } from '../kernel/errors.js'\nimport { loadKeyring } from '../with-party/team/keyring.js'\nimport { loadFence } from '../with-shape/schema-update/fence.js'\nimport { deriveSchemaManifest, type LookupDEK } from '../with-shape/manifest/derive.js'\nimport type { SchemaManifest } from '../with-shape/manifest/types.js'\n\nexport interface OpenPodOptions {\n /** The ciphertext store to restore into (and where the opened `Vault` lives). */\n readonly store: NoydbStore\n /**\n * The target vault name. The pod header deliberately carries no vault\n * name (minimum-disclosure — `NoydbPodHeader`'s forbidden-fields list),\n * so the caller names the vault to restore into/open.\n */\n readonly vault: string\n /** User identifier — passed through to `createNoydb`. */\n readonly user: string\n /** That user's secret — passed through to `createNoydb` / `loadKeyring`. */\n readonly secret: string | EchoSecretParts\n /**\n * Trusted signing keys (`keyId → publicKeyB64`). When supplied,\n * `verifyPodHeader` runs and its result is returned as `verification`.\n * Omitted → no verification attempted, `verification` is absent.\n */\n readonly trustedKeys?: Readonly<Record<string, string>>\n /**\n * Passthrough to `createNoydb` for every option beyond `store`/`user`/\n * `secret` (history/indexing/blobs/… strategies, `secretMode`, etc.).\n */\n readonly noydbOptions?: Omit<NoydbOptions, 'store' | 'user' | 'secret'>\n /**\n * Dev override for the generation-ahead fence (AC #4). Default `false`:\n * a pod whose schema manifest generation is ahead of this store's\n * pre-restore fence generation throws `MigrationRequiredError`. `true`\n * opens anyway, logging a console warning.\n */\n readonly allowGenerationAhead?: boolean\n}\n\nexport interface OpenPodResult {\n readonly db: Noydb\n readonly vault: Vault\n readonly header: NoydbPodHeader\n /** Present iff `opts.trustedKeys` was supplied. */\n readonly verification?: PodVerifyResult\n /** The pod's schema manifest, re-derived from the just-restored `_schemas/*`. */\n readonly manifest?: SchemaManifest\n}\n\n/**\n * Open a `.noydb` pod: verify its integrity (and, opt-in, its signature),\n * unlock + restore it into `opts.vault`, and re-derive its schema manifest.\n * See the module doc for the full flow and the errors each step can throw.\n */\nexport async function open(podFileOrBytes: Uint8Array, opts: OpenPodOptions): Promise<OpenPodResult> {\n const { header, dumpJson } = await readPod(podFileOrBytes)\n\n let verification: PodVerifyResult | undefined\n if (opts.trustedKeys !== undefined) {\n verification = await verifyPodHeader(podFileOrBytes, opts.trustedKeys)\n if (verification.status === 'untrusted' || verification.status === 'tampered') {\n throw new PodHeaderVerificationError(verification.status, verification.keyId)\n }\n }\n\n // Reader's pre-restore generation snapshot (see module doc, step 5) —\n // must be read from the raw store BEFORE vault.load() overwrites it.\n const readerGeneration = (await loadFence(opts.store, opts.vault)).currentSchemaVersion\n\n const db = await createNoydb({ ...opts.noydbOptions, store: opts.store, user: opts.user, secret: opts.secret })\n const vault = await db.openVault(opts.vault)\n await vault.load(dumpJson)\n\n // #941 review fix: a NON-MINTING lookup, not `ensureCollectionDEK` (which\n // mints+persists a fresh DEK for any collection absent from the keyring).\n // open() is a READ path — deriveSchemaManifest only ever iterates\n // collections that already have a `_schemas` envelope, so an absent DEK\n // here means \"this principal cannot decrypt this sibling,\" never \"new\n // collection.\" The old minting call polluted a scoped principal's just-\n // restored `_keyring/<user>` file with garbage sibling DEKs on every\n // open(). See `derive.ts`'s `LookupDEK` doc for the full rationale.\n const keyring = await loadKeyring(opts.store, opts.vault, { userId: opts.user, secret: opts.secret })\n const lookupDEK: LookupDEK = async (name) => keyring.deks.get(name)\n const { manifest, undecodableCollections } = await deriveSchemaManifest(opts.store, opts.vault, lookupDEK)\n\n // A scoped principal's `manifest` is legitimately their own visible\n // slice (see `sync.ts`'s module doc) — `open()` is a per-principal read,\n // not an assertion of pod-wide completeness. `undecodableCollections` is\n // intentionally not surfaced on `OpenPodResult` (out of this fix's\n // scope); callers that need it can call `deriveSchemaManifest` directly.\n void undecodableCollections\n\n // See module doc, step 5 — the pod's generation signal is the highest\n // per-collection stamp, not the (locally re-derived, fence-driven)\n // top-level `manifest.generation`.\n const podGeneration = Object.values(manifest.collections)\n .reduce((max, entry) => Math.max(max, entry.generation), 0)\n\n if (podGeneration > readerGeneration) {\n if (!opts.allowGenerationAhead) {\n throw new MigrationRequiredError(\n `open(): pod's schema generation (${podGeneration}) is ahead of vault ` +\n `\"${opts.vault}\"'s local generation (${readerGeneration}). Pass ` +\n `\\`allowGenerationAhead: true\\` to open anyway, or reconcile the schema first.`,\n )\n }\n console.warn(\n `[noy-db] open(): vault \"${opts.vault}\" opened with allowGenerationAhead — pod schema ` +\n `generation ${podGeneration} is ahead of the local generation ${readerGeneration}.`,\n )\n } else if (podGeneration < readerGeneration) {\n // #941 review (Important 3, AC #4): divergence in EITHER direction\n // should be observable, not just the ahead-bypass case above. The\n // reader being ahead of the pod is non-fatal (the pod is simply older\n // content) but silent coexistence divergence is exactly what AC #4\n // asks to surface — same `[noy-db]`-prefixed console.warn facility as\n // `sync.ts`'s cap-exhaustion warning.\n console.warn(\n `[noy-db] open(): vault \"${opts.vault}\"'s local schema generation (${readerGeneration}) is ` +\n `ahead of the pod's (${podGeneration}) — the pod reflects an earlier schema than this reader.`,\n )\n }\n\n return { db, vault, header, manifest, ...(verification !== undefined ? { verification } : {}) }\n}\n"],"mappings":"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","names":[]}