@noy-db/hub 0.3.0-pre.12 → 0.3.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 (373) hide show
  1. package/dist/aggregate/index.js +4 -4
  2. package/dist/api-ZGS6LCOF.js +20 -0
  3. package/dist/as/index.js +11 -11
  4. package/dist/attestation/index.js +18 -18
  5. package/dist/{backup-2WVBIVXW.js → backup-BJ57IKAO.js} +28 -11
  6. package/dist/backup-BJ57IKAO.js.map +1 -0
  7. package/dist/blobs/index.js +12 -10
  8. package/dist/blobs/index.js.map +1 -1
  9. package/dist/broker/index.js +12 -12
  10. package/dist/bundle/index.js +25 -22
  11. package/dist/bundle/index.js.map +1 -1
  12. package/dist/by/index.js +2 -2
  13. package/dist/cargo/index.js +22 -22
  14. package/dist/{chunk-ZL6PRULX.js → chunk-2NVOWNI3.js} +4 -4
  15. package/dist/{chunk-WEHIQYQH.js → chunk-3B5VVCNY.js} +2 -2
  16. package/dist/{chunk-FF7S3XDW.js → chunk-3LQ2YKO4.js} +2 -2
  17. package/dist/{chunk-NAR6IDKH.js → chunk-3QCRBHCY.js} +2 -2
  18. package/dist/{chunk-SXPCZ6LL.js → chunk-454JXE57.js} +3 -3
  19. package/dist/{chunk-R3NHNVS5.js → chunk-4CTHCN4J.js} +12 -10
  20. package/dist/{chunk-R3NHNVS5.js.map → chunk-4CTHCN4J.js.map} +1 -1
  21. package/dist/{chunk-JGUZMC3I.js → chunk-4JBYV75E.js} +5 -5
  22. package/dist/{chunk-RXTMLYJS.js → chunk-4NXKCLLQ.js} +2 -2
  23. package/dist/{chunk-GXLZXD2W.js → chunk-4VNQOLML.js} +2 -2
  24. package/dist/{chunk-45QPICLE.js → chunk-5M5DVGV7.js} +2 -2
  25. package/dist/{chunk-EHW5KTTA.js → chunk-5O3XF4DV.js} +4 -4
  26. package/dist/chunk-5O3XF4DV.js.map +1 -0
  27. package/dist/{chunk-S2OWPVSO.js → chunk-5YOLBEQ6.js} +2 -2
  28. package/dist/{chunk-BDEMZD4F.js → chunk-6CRIV7DE.js} +2 -2
  29. package/dist/{chunk-VCUQR6ZL.js → chunk-6JTIAGHD.js} +3 -3
  30. package/dist/{chunk-K2DLLK34.js → chunk-6QTNZJBS.js} +3 -3
  31. package/dist/{chunk-UMC5QZTX.js → chunk-6T2SCQRU.js} +4 -4
  32. package/dist/{chunk-J3JIYFYL.js → chunk-7DSTRQIJ.js} +2 -2
  33. package/dist/{chunk-3QSOFZQB.js → chunk-AUSTCW2X.js} +10 -10
  34. package/dist/{chunk-PN5332SU.js → chunk-B3HJYUOF.js} +2 -2
  35. package/dist/{chunk-DIB2BOIH.js → chunk-BDVFPG6Y.js} +5 -5
  36. package/dist/{chunk-FZCGIJEJ.js → chunk-BIOUI4HL.js} +50 -1
  37. package/dist/chunk-BIOUI4HL.js.map +1 -0
  38. package/dist/{chunk-FYNE4WLC.js → chunk-CAF6WOK7.js} +2 -2
  39. package/dist/{chunk-AKEOHKMT.js → chunk-CDUQTA3Z.js} +110 -11
  40. package/dist/chunk-CDUQTA3Z.js.map +1 -0
  41. package/dist/{chunk-37WOJQWY.js → chunk-CIE2BASH.js} +2 -2
  42. package/dist/{chunk-BLUT46UP.js → chunk-CRI4OUV4.js} +2 -2
  43. package/dist/{chunk-Y5TWPBNW.js → chunk-DLG74CNB.js} +4 -4
  44. package/dist/{chunk-HIOLCOK3.js → chunk-E5ZQT5KX.js} +2 -2
  45. package/dist/{chunk-IQIVD7TP.js → chunk-EQO2UVP4.js} +6 -6
  46. package/dist/{chunk-US34HTOI.js → chunk-ERJ7FU55.js} +3 -3
  47. package/dist/{chunk-S7TAPIRS.js → chunk-EVVGJDRQ.js} +3 -3
  48. package/dist/{chunk-EXGDL2UQ.js → chunk-EVX47VBM.js} +2 -2
  49. package/dist/{chunk-NT2ZO4KI.js → chunk-EXK7QRWF.js} +5 -5
  50. package/dist/{chunk-7QLY64AD.js → chunk-EYZ6IQ6X.js} +5 -5
  51. package/dist/{chunk-QF5LGMSV.js → chunk-F64IRYG3.js} +2 -2
  52. package/dist/{chunk-PH7G7ZI7.js → chunk-FHS2HQSH.js} +5 -5
  53. package/dist/{chunk-HG76BZ6G.js → chunk-FM3G3NOY.js} +27 -9
  54. package/dist/chunk-FM3G3NOY.js.map +1 -0
  55. package/dist/{chunk-ZHOJOT6S.js → chunk-FUG4SFN3.js} +2 -2
  56. package/dist/{chunk-JRKE5MAZ.js → chunk-GKEWXIV5.js} +2 -2
  57. package/dist/{chunk-6MA575R5.js → chunk-GTGPO5KW.js} +5 -5
  58. package/dist/{chunk-XS6DLN67.js → chunk-HB3RFQSC.js} +3 -3
  59. package/dist/{chunk-VXZNSPJM.js → chunk-HDMEGAQE.js} +5 -5
  60. package/dist/chunk-HDQIIUPH.js +135 -0
  61. package/dist/chunk-HDQIIUPH.js.map +1 -0
  62. package/dist/{chunk-DJ7MRXNA.js → chunk-HTJQYHZP.js} +2 -2
  63. package/dist/{chunk-NRYNRSGV.js → chunk-I6GALP6E.js} +6 -6
  64. package/dist/{chunk-GL3JV5EV.js → chunk-IL6POWCW.js} +6 -6
  65. package/dist/{chunk-NEYKOESM.js → chunk-IU6SNYX6.js} +5 -5
  66. package/dist/{chunk-CCIR2OJH.js → chunk-J2ZX2XUV.js} +5 -5
  67. package/dist/{chunk-F3LGBRSV.js → chunk-JIF24JPM.js} +57 -15
  68. package/dist/chunk-JIF24JPM.js.map +1 -0
  69. package/dist/{chunk-NM3IFKBL.js → chunk-JKJOGPZN.js} +2 -2
  70. package/dist/{chunk-JC75UBMT.js → chunk-JNAI2HN2.js} +1 -1
  71. package/dist/{chunk-JC75UBMT.js.map → chunk-JNAI2HN2.js.map} +1 -1
  72. package/dist/{chunk-TVS6BUUR.js → chunk-JZUV47FZ.js} +2 -2
  73. package/dist/{chunk-5HNX4OD4.js → chunk-K5IMOGHB.js} +2 -2
  74. package/dist/{chunk-XJ47GS53.js → chunk-KSKBRKGJ.js} +3 -3
  75. package/dist/{chunk-INBQ43DI.js → chunk-LASZKVMO.js} +3 -3
  76. package/dist/{chunk-EVHBK7BN.js → chunk-LXEXBTKL.js} +4 -4
  77. package/dist/{chunk-6S7ENMRE.js → chunk-M4CV6BRT.js} +2 -2
  78. package/dist/{chunk-AAIIDAUC.js → chunk-MEFJPTZ4.js} +3 -3
  79. package/dist/{chunk-WHQHTNT7.js → chunk-MV43JSJF.js} +4 -4
  80. package/dist/{chunk-F44XWXOA.js → chunk-NJ4EOKMB.js} +4 -4
  81. package/dist/{chunk-ILF5GARW.js → chunk-NR5LT5RY.js} +7 -7
  82. package/dist/{chunk-TBNF5DV5.js → chunk-NWSGI5C7.js} +3 -3
  83. package/dist/{chunk-BDPAVYGJ.js → chunk-OSDB5WW3.js} +92 -15
  84. package/dist/chunk-OSDB5WW3.js.map +1 -0
  85. package/dist/chunk-PN2XMATY.js +37 -0
  86. package/dist/chunk-PN2XMATY.js.map +1 -0
  87. package/dist/{chunk-EIZ4YOG2.js → chunk-Q5VNKNE5.js} +3 -3
  88. package/dist/{chunk-TVOHSPVK.js → chunk-QLWMEXCT.js} +2 -2
  89. package/dist/{chunk-7GAXFU2O.js → chunk-QRB7NAOJ.js} +2 -2
  90. package/dist/{chunk-X6Y5MXTH.js → chunk-R4ROFEYC.js} +2 -2
  91. package/dist/{chunk-4BQXMCE5.js → chunk-RCVJZMFE.js} +4 -4
  92. package/dist/{chunk-QG5RQQBO.js → chunk-RIX4V2TB.js} +3 -3
  93. package/dist/{chunk-SAL5X5OK.js → chunk-RYCTDFUX.js} +2 -2
  94. package/dist/{chunk-CXC3JLAQ.js → chunk-RZ5SVOYR.js} +2 -2
  95. package/dist/{chunk-44IL5G3W.js → chunk-SLFHQTTW.js} +5 -5
  96. package/dist/chunk-SNZ6URZZ.js +2782 -0
  97. package/dist/chunk-SNZ6URZZ.js.map +1 -0
  98. package/dist/{chunk-2MTRJCBI.js → chunk-T2FFWNPK.js} +3 -3
  99. package/dist/{chunk-QYPDYN23.js → chunk-UNFPPVNI.js} +2 -2
  100. package/dist/{chunk-RNXJ2VO6.js → chunk-VFUPHI2Z.js} +63 -10
  101. package/dist/chunk-VFUPHI2Z.js.map +1 -0
  102. package/dist/{chunk-7ZOSHEVS.js → chunk-VLFCJYTL.js} +9 -9
  103. package/dist/{chunk-APHLCXVC.js → chunk-VN3DBQAU.js} +2 -2
  104. package/dist/{chunk-HK4XTXO4.js → chunk-VZRODIM3.js} +4 -4
  105. package/dist/{chunk-P2LJI6JE.js → chunk-W22IKJE2.js} +2 -2
  106. package/dist/{chunk-FJTWLQV3.js → chunk-X3YYQWOQ.js} +3 -3
  107. package/dist/{chunk-YCXKFB7J.js → chunk-X7QBD67R.js} +498 -274
  108. package/dist/chunk-X7QBD67R.js.map +1 -0
  109. package/dist/{chunk-HA6ZQWA2.js → chunk-XDYREZWE.js} +82 -10
  110. package/dist/chunk-XDYREZWE.js.map +1 -0
  111. package/dist/{chunk-HBBZWEC5.js → chunk-XWZVFISC.js} +40 -6
  112. package/dist/{chunk-HBBZWEC5.js.map → chunk-XWZVFISC.js.map} +1 -1
  113. package/dist/{chunk-SGJGWBSC.js → chunk-YFXZEAYK.js} +6 -6
  114. package/dist/{chunk-SIAIYP6G.js → chunk-ZAJAVEXY.js} +2 -2
  115. package/dist/{chunk-KKYEZCQG.js → chunk-ZICD77GM.js} +2 -2
  116. package/dist/{chunk-HGAB325C.js → chunk-ZJ2AK4V7.js} +2 -2
  117. package/dist/{chunk-HYG32HCM.js → chunk-ZUODRVHY.js} +2 -2
  118. package/dist/classified/index.js +5 -5
  119. package/dist/{classified-marker-M7K3OJPM.js → classified-marker-CZKA6ITE.js} +3 -3
  120. package/dist/collection-facade-TLWIPI76.js +44 -0
  121. package/dist/{computed-ORCGM5JE.js → computed-ZIQTYTDI.js} +3 -3
  122. package/dist/consent/index.js +9 -9
  123. package/dist/{dead-filter-OV5FGKOG.js → dead-filter-ESOBZZP5.js} +2 -2
  124. package/dist/delegation-W7ZV2322.js +25 -0
  125. package/dist/derivations/index.js +12 -12
  126. package/dist/derive-H2BJTLUI.js +21 -0
  127. package/dist/{enclave-XXFUKJC6.js → enclave-AP3I5W7O.js} +15 -9
  128. package/dist/executor-KGWTNEQD.js +9 -0
  129. package/dist/executor-OVEXR6RT.js +9 -0
  130. package/dist/executor-UNEH5ZYH.js +25 -0
  131. package/dist/export-accessible-PCMXSUZS.js +23 -0
  132. package/dist/extract-partition-SWHFSYJO.js +38 -0
  133. package/dist/{fanout-sidecar-IO2KDUWV.js → fanout-sidecar-A765THMX.js} +9 -9
  134. package/dist/find-N6345CNT.js +11 -0
  135. package/dist/forget/index.js +9 -9
  136. package/dist/guards/index.js +3 -3
  137. package/dist/history/index.js +14 -11
  138. package/dist/history/index.js.map +1 -1
  139. package/dist/i18n/index.js +14 -14
  140. package/dist/index.d.ts +5 -1
  141. package/dist/index.js +108 -89
  142. package/dist/index.js.map +1 -1
  143. package/dist/indexing/index.js +2 -2
  144. package/dist/issue-5OKB6I2R.js +19 -0
  145. package/dist/kernel/collection-config.d.ts +20 -0
  146. package/dist/kernel/collection.d.ts +31 -28
  147. package/dist/kernel/enclave/classify/verify.d.ts +3 -0
  148. package/dist/kernel/enclave/index.d.ts +2 -1
  149. package/dist/kernel/enclave/record-keys/deterministic.d.ts +1 -0
  150. package/dist/kernel/enclave/record-keys/lifecycle.d.ts +50 -0
  151. package/dist/kernel/enclave/record-keys/record-codec.d.ts +27 -0
  152. package/dist/kernel/errors.d.ts +74 -0
  153. package/dist/kernel/lazy-count.d.ts +13 -0
  154. package/dist/kernel/tier-visibility.d.ts +54 -0
  155. package/dist/kernel/types.d.ts +73 -0
  156. package/dist/kernel/vault.d.ts +8 -5
  157. package/dist/kernel/via/dispatch.d.ts +10 -0
  158. package/dist/{ledger-UAQKPWNM.js → ledger-GFBKVPV4.js} +11 -11
  159. package/dist/legacy/bundle.d.ts +1 -1
  160. package/dist/liberate-3VDOT4KO.js +24 -0
  161. package/dist/{link-set-DGYUOFVD.js → link-set-Z7Q2ASFX.js} +11 -11
  162. package/dist/materialized-views/index.js +18 -18
  163. package/dist/noydb-ZCJOYBDI.js +68 -0
  164. package/dist/on/index.js +9 -9
  165. package/dist/overlay-views/index.js +4 -4
  166. package/dist/periods/index.js +12 -12
  167. package/dist/pod/index.js +11 -11
  168. package/dist/{policy-5POYLI26.js → policy-Z3RUAIY2.js} +5 -5
  169. package/dist/port/with/blob-strategy.d.ts +17 -0
  170. package/dist/portability/index.js +8 -8
  171. package/dist/{post-register-LB3P4XQU.js → post-register-JTH76BLY.js} +6 -6
  172. package/dist/{public-envelope-GOG4LUM7.js → public-envelope-B4KNWVUA.js} +4 -4
  173. package/dist/query/index.js +5 -5
  174. package/dist/register-VAHONCID.js +23 -0
  175. package/dist/registry-7MLVD3VO.js +19 -0
  176. package/dist/registry-EPKPD5SM.js +18 -0
  177. package/dist/registry-P5T7PS2U.js +9 -0
  178. package/dist/request-withdrawal-TI3A5WPS.js +31 -0
  179. package/dist/{reveal-NPVROV4D.js → reveal-DIBHLUZX.js} +7 -7
  180. package/dist/reveal-DIBHLUZX.js.map +1 -0
  181. package/dist/revoke-FDRD6GRW.js +24 -0
  182. package/dist/satellites/index.js +1 -1
  183. package/dist/sealed-record/index.js +12 -12
  184. package/dist/{seed-VI4FU7UH.js → seed-Q6M2IZFL.js} +11 -11
  185. package/dist/session/index.js +9 -9
  186. package/dist/shadow/index.js +2 -2
  187. package/dist/signer-MUWEIUI6.js +25 -0
  188. package/dist/snapshots/index.js +10 -10
  189. package/dist/stale-OSQ7J3AA.js +29 -0
  190. package/dist/storage-MVFCGEQE.js +23 -0
  191. package/dist/{store-coordination-provider-5KDS2RRP.js → store-coordination-provider-64OEZFGS.js} +3 -3
  192. package/dist/sync/index.js +9 -9
  193. package/dist/team/index.js +15 -15
  194. package/dist/tiers/index.js +14 -10
  195. package/dist/to/index.js +1 -1
  196. package/dist/tx/index.js +3 -3
  197. package/dist/util/index.js +1 -1
  198. package/dist/{verify-QK76NJ4G.js → verify-ZASTRNPR.js} +9 -8
  199. package/dist/verify-ZASTRNPR.js.map +1 -0
  200. package/dist/{walk-XQCBKSBW.js → walk-SVNDIZ62.js} +11 -11
  201. package/dist/with/index.js +11 -11
  202. package/dist/with-audit/forget/strategy.d.ts +22 -0
  203. package/dist/with-audit/tiers/index.d.ts +275 -3
  204. package/dist/with-audit/tiers/strategy.d.ts +5 -4
  205. package/dist/with-cargo/describe-extraction.d.ts +9 -1
  206. package/dist/with-cargo/extract-partition.d.ts +9 -1
  207. package/dist/with-cargo/walk-closure.d.ts +32 -3
  208. package/dist/with-commit/history/history.d.ts +46 -0
  209. package/dist/with-commit/history/ledger/store.d.ts +21 -0
  210. package/dist/with-commit/history/strategy.d.ts +10 -0
  211. package/dist/with-commit/history/time-machine.d.ts +23 -0
  212. package/dist/with-commit/tx/elevated-handle.d.ts +2 -1
  213. package/dist/with-formula/materialized-views/executor.d.ts +9 -0
  214. package/dist/with-formula/materialized-views/stale.d.ts +52 -1
  215. package/dist/with-lookup/embeddings/index.d.ts +1 -0
  216. package/dist/with-lookup/embeddings/vec-id.d.ts +47 -0
  217. package/dist/with-lookup/indexing/collection-facade.d.ts +38 -1
  218. package/dist/with-lookup/search/collection-facade.d.ts +40 -0
  219. package/dist/with-lookup/search/persisted-index-store.d.ts +82 -2
  220. package/dist/with-lookup/search/strategy.d.ts +4 -0
  221. package/dist/with-shape/blobs/blob-intent.d.ts +153 -0
  222. package/dist/with-shape/blobs/blob-set.d.ts +949 -5
  223. package/dist/withdraw-accessible-WZIEVWSR.js +28 -0
  224. package/package.json +3 -3
  225. package/dist/api-3TH2GOFQ.js +0 -20
  226. package/dist/backup-2WVBIVXW.js.map +0 -1
  227. package/dist/chunk-65VLRG3I.js +0 -54
  228. package/dist/chunk-65VLRG3I.js.map +0 -1
  229. package/dist/chunk-AKEOHKMT.js.map +0 -1
  230. package/dist/chunk-BDPAVYGJ.js.map +0 -1
  231. package/dist/chunk-CZMCOFEA.js +0 -1221
  232. package/dist/chunk-CZMCOFEA.js.map +0 -1
  233. package/dist/chunk-EHW5KTTA.js.map +0 -1
  234. package/dist/chunk-F3LGBRSV.js.map +0 -1
  235. package/dist/chunk-FZCGIJEJ.js.map +0 -1
  236. package/dist/chunk-HA6ZQWA2.js.map +0 -1
  237. package/dist/chunk-HG76BZ6G.js.map +0 -1
  238. package/dist/chunk-RNXJ2VO6.js.map +0 -1
  239. package/dist/chunk-YCXKFB7J.js.map +0 -1
  240. package/dist/collection-facade-DWA735G2.js +0 -39
  241. package/dist/delegation-YVSHMIZZ.js +0 -25
  242. package/dist/derive-BUO555L5.js +0 -21
  243. package/dist/executor-6OCCE5VM.js +0 -25
  244. package/dist/executor-JFVAJY42.js +0 -9
  245. package/dist/executor-UPSH5O3F.js +0 -9
  246. package/dist/export-accessible-L4A2QZA5.js +0 -23
  247. package/dist/extract-partition-ZHQ4RGGM.js +0 -36
  248. package/dist/find-ZFZDZQ2U.js +0 -11
  249. package/dist/issue-OG4V23SQ.js +0 -19
  250. package/dist/liberate-GFUQJRK5.js +0 -24
  251. package/dist/noydb-AKH4AMCR.js +0 -67
  252. package/dist/register-ZI7YI7US.js +0 -23
  253. package/dist/registry-DZ2T4LL7.js +0 -9
  254. package/dist/registry-NVWEQ2GU.js +0 -18
  255. package/dist/registry-SEKL74ET.js +0 -19
  256. package/dist/request-withdrawal-ZP3JIRVS.js +0 -31
  257. package/dist/reveal-NPVROV4D.js.map +0 -1
  258. package/dist/revoke-VF6TAJK2.js +0 -24
  259. package/dist/signer-DRDDCCHW.js +0 -25
  260. package/dist/stale-TJXFQKOJ.js +0 -14
  261. package/dist/storage-GR7KZRH4.js +0 -23
  262. package/dist/verify-QK76NJ4G.js.map +0 -1
  263. package/dist/withdraw-accessible-FMN4LPJZ.js +0 -28
  264. /package/dist/{api-3TH2GOFQ.js.map → api-ZGS6LCOF.js.map} +0 -0
  265. /package/dist/{chunk-ZL6PRULX.js.map → chunk-2NVOWNI3.js.map} +0 -0
  266. /package/dist/{chunk-WEHIQYQH.js.map → chunk-3B5VVCNY.js.map} +0 -0
  267. /package/dist/{chunk-FF7S3XDW.js.map → chunk-3LQ2YKO4.js.map} +0 -0
  268. /package/dist/{chunk-NAR6IDKH.js.map → chunk-3QCRBHCY.js.map} +0 -0
  269. /package/dist/{chunk-SXPCZ6LL.js.map → chunk-454JXE57.js.map} +0 -0
  270. /package/dist/{chunk-JGUZMC3I.js.map → chunk-4JBYV75E.js.map} +0 -0
  271. /package/dist/{chunk-RXTMLYJS.js.map → chunk-4NXKCLLQ.js.map} +0 -0
  272. /package/dist/{chunk-GXLZXD2W.js.map → chunk-4VNQOLML.js.map} +0 -0
  273. /package/dist/{chunk-45QPICLE.js.map → chunk-5M5DVGV7.js.map} +0 -0
  274. /package/dist/{chunk-S2OWPVSO.js.map → chunk-5YOLBEQ6.js.map} +0 -0
  275. /package/dist/{chunk-BDEMZD4F.js.map → chunk-6CRIV7DE.js.map} +0 -0
  276. /package/dist/{chunk-VCUQR6ZL.js.map → chunk-6JTIAGHD.js.map} +0 -0
  277. /package/dist/{chunk-K2DLLK34.js.map → chunk-6QTNZJBS.js.map} +0 -0
  278. /package/dist/{chunk-UMC5QZTX.js.map → chunk-6T2SCQRU.js.map} +0 -0
  279. /package/dist/{chunk-J3JIYFYL.js.map → chunk-7DSTRQIJ.js.map} +0 -0
  280. /package/dist/{chunk-3QSOFZQB.js.map → chunk-AUSTCW2X.js.map} +0 -0
  281. /package/dist/{chunk-PN5332SU.js.map → chunk-B3HJYUOF.js.map} +0 -0
  282. /package/dist/{chunk-DIB2BOIH.js.map → chunk-BDVFPG6Y.js.map} +0 -0
  283. /package/dist/{chunk-FYNE4WLC.js.map → chunk-CAF6WOK7.js.map} +0 -0
  284. /package/dist/{chunk-37WOJQWY.js.map → chunk-CIE2BASH.js.map} +0 -0
  285. /package/dist/{chunk-BLUT46UP.js.map → chunk-CRI4OUV4.js.map} +0 -0
  286. /package/dist/{chunk-Y5TWPBNW.js.map → chunk-DLG74CNB.js.map} +0 -0
  287. /package/dist/{chunk-HIOLCOK3.js.map → chunk-E5ZQT5KX.js.map} +0 -0
  288. /package/dist/{chunk-IQIVD7TP.js.map → chunk-EQO2UVP4.js.map} +0 -0
  289. /package/dist/{chunk-US34HTOI.js.map → chunk-ERJ7FU55.js.map} +0 -0
  290. /package/dist/{chunk-S7TAPIRS.js.map → chunk-EVVGJDRQ.js.map} +0 -0
  291. /package/dist/{chunk-EXGDL2UQ.js.map → chunk-EVX47VBM.js.map} +0 -0
  292. /package/dist/{chunk-NT2ZO4KI.js.map → chunk-EXK7QRWF.js.map} +0 -0
  293. /package/dist/{chunk-7QLY64AD.js.map → chunk-EYZ6IQ6X.js.map} +0 -0
  294. /package/dist/{chunk-QF5LGMSV.js.map → chunk-F64IRYG3.js.map} +0 -0
  295. /package/dist/{chunk-PH7G7ZI7.js.map → chunk-FHS2HQSH.js.map} +0 -0
  296. /package/dist/{chunk-ZHOJOT6S.js.map → chunk-FUG4SFN3.js.map} +0 -0
  297. /package/dist/{chunk-JRKE5MAZ.js.map → chunk-GKEWXIV5.js.map} +0 -0
  298. /package/dist/{chunk-6MA575R5.js.map → chunk-GTGPO5KW.js.map} +0 -0
  299. /package/dist/{chunk-XS6DLN67.js.map → chunk-HB3RFQSC.js.map} +0 -0
  300. /package/dist/{chunk-VXZNSPJM.js.map → chunk-HDMEGAQE.js.map} +0 -0
  301. /package/dist/{chunk-DJ7MRXNA.js.map → chunk-HTJQYHZP.js.map} +0 -0
  302. /package/dist/{chunk-NRYNRSGV.js.map → chunk-I6GALP6E.js.map} +0 -0
  303. /package/dist/{chunk-GL3JV5EV.js.map → chunk-IL6POWCW.js.map} +0 -0
  304. /package/dist/{chunk-NEYKOESM.js.map → chunk-IU6SNYX6.js.map} +0 -0
  305. /package/dist/{chunk-CCIR2OJH.js.map → chunk-J2ZX2XUV.js.map} +0 -0
  306. /package/dist/{chunk-NM3IFKBL.js.map → chunk-JKJOGPZN.js.map} +0 -0
  307. /package/dist/{chunk-TVS6BUUR.js.map → chunk-JZUV47FZ.js.map} +0 -0
  308. /package/dist/{chunk-5HNX4OD4.js.map → chunk-K5IMOGHB.js.map} +0 -0
  309. /package/dist/{chunk-XJ47GS53.js.map → chunk-KSKBRKGJ.js.map} +0 -0
  310. /package/dist/{chunk-INBQ43DI.js.map → chunk-LASZKVMO.js.map} +0 -0
  311. /package/dist/{chunk-EVHBK7BN.js.map → chunk-LXEXBTKL.js.map} +0 -0
  312. /package/dist/{chunk-6S7ENMRE.js.map → chunk-M4CV6BRT.js.map} +0 -0
  313. /package/dist/{chunk-AAIIDAUC.js.map → chunk-MEFJPTZ4.js.map} +0 -0
  314. /package/dist/{chunk-WHQHTNT7.js.map → chunk-MV43JSJF.js.map} +0 -0
  315. /package/dist/{chunk-F44XWXOA.js.map → chunk-NJ4EOKMB.js.map} +0 -0
  316. /package/dist/{chunk-ILF5GARW.js.map → chunk-NR5LT5RY.js.map} +0 -0
  317. /package/dist/{chunk-TBNF5DV5.js.map → chunk-NWSGI5C7.js.map} +0 -0
  318. /package/dist/{chunk-EIZ4YOG2.js.map → chunk-Q5VNKNE5.js.map} +0 -0
  319. /package/dist/{chunk-TVOHSPVK.js.map → chunk-QLWMEXCT.js.map} +0 -0
  320. /package/dist/{chunk-7GAXFU2O.js.map → chunk-QRB7NAOJ.js.map} +0 -0
  321. /package/dist/{chunk-X6Y5MXTH.js.map → chunk-R4ROFEYC.js.map} +0 -0
  322. /package/dist/{chunk-4BQXMCE5.js.map → chunk-RCVJZMFE.js.map} +0 -0
  323. /package/dist/{chunk-QG5RQQBO.js.map → chunk-RIX4V2TB.js.map} +0 -0
  324. /package/dist/{chunk-SAL5X5OK.js.map → chunk-RYCTDFUX.js.map} +0 -0
  325. /package/dist/{chunk-CXC3JLAQ.js.map → chunk-RZ5SVOYR.js.map} +0 -0
  326. /package/dist/{chunk-44IL5G3W.js.map → chunk-SLFHQTTW.js.map} +0 -0
  327. /package/dist/{chunk-2MTRJCBI.js.map → chunk-T2FFWNPK.js.map} +0 -0
  328. /package/dist/{chunk-QYPDYN23.js.map → chunk-UNFPPVNI.js.map} +0 -0
  329. /package/dist/{chunk-7ZOSHEVS.js.map → chunk-VLFCJYTL.js.map} +0 -0
  330. /package/dist/{chunk-APHLCXVC.js.map → chunk-VN3DBQAU.js.map} +0 -0
  331. /package/dist/{chunk-HK4XTXO4.js.map → chunk-VZRODIM3.js.map} +0 -0
  332. /package/dist/{chunk-P2LJI6JE.js.map → chunk-W22IKJE2.js.map} +0 -0
  333. /package/dist/{chunk-FJTWLQV3.js.map → chunk-X3YYQWOQ.js.map} +0 -0
  334. /package/dist/{chunk-SGJGWBSC.js.map → chunk-YFXZEAYK.js.map} +0 -0
  335. /package/dist/{chunk-SIAIYP6G.js.map → chunk-ZAJAVEXY.js.map} +0 -0
  336. /package/dist/{chunk-KKYEZCQG.js.map → chunk-ZICD77GM.js.map} +0 -0
  337. /package/dist/{chunk-HGAB325C.js.map → chunk-ZJ2AK4V7.js.map} +0 -0
  338. /package/dist/{chunk-HYG32HCM.js.map → chunk-ZUODRVHY.js.map} +0 -0
  339. /package/dist/{classified-marker-M7K3OJPM.js.map → classified-marker-CZKA6ITE.js.map} +0 -0
  340. /package/dist/{collection-facade-DWA735G2.js.map → collection-facade-TLWIPI76.js.map} +0 -0
  341. /package/dist/{computed-ORCGM5JE.js.map → computed-ZIQTYTDI.js.map} +0 -0
  342. /package/dist/{dead-filter-OV5FGKOG.js.map → dead-filter-ESOBZZP5.js.map} +0 -0
  343. /package/dist/{delegation-YVSHMIZZ.js.map → delegation-W7ZV2322.js.map} +0 -0
  344. /package/dist/{derive-BUO555L5.js.map → derive-H2BJTLUI.js.map} +0 -0
  345. /package/dist/{enclave-XXFUKJC6.js.map → enclave-AP3I5W7O.js.map} +0 -0
  346. /package/dist/{executor-6OCCE5VM.js.map → executor-KGWTNEQD.js.map} +0 -0
  347. /package/dist/{executor-JFVAJY42.js.map → executor-OVEXR6RT.js.map} +0 -0
  348. /package/dist/{executor-UPSH5O3F.js.map → executor-UNEH5ZYH.js.map} +0 -0
  349. /package/dist/{export-accessible-L4A2QZA5.js.map → export-accessible-PCMXSUZS.js.map} +0 -0
  350. /package/dist/{extract-partition-ZHQ4RGGM.js.map → extract-partition-SWHFSYJO.js.map} +0 -0
  351. /package/dist/{fanout-sidecar-IO2KDUWV.js.map → fanout-sidecar-A765THMX.js.map} +0 -0
  352. /package/dist/{find-ZFZDZQ2U.js.map → find-N6345CNT.js.map} +0 -0
  353. /package/dist/{issue-OG4V23SQ.js.map → issue-5OKB6I2R.js.map} +0 -0
  354. /package/dist/{ledger-UAQKPWNM.js.map → ledger-GFBKVPV4.js.map} +0 -0
  355. /package/dist/{liberate-GFUQJRK5.js.map → liberate-3VDOT4KO.js.map} +0 -0
  356. /package/dist/{link-set-DGYUOFVD.js.map → link-set-Z7Q2ASFX.js.map} +0 -0
  357. /package/dist/{noydb-AKH4AMCR.js.map → noydb-ZCJOYBDI.js.map} +0 -0
  358. /package/dist/{policy-5POYLI26.js.map → policy-Z3RUAIY2.js.map} +0 -0
  359. /package/dist/{post-register-LB3P4XQU.js.map → post-register-JTH76BLY.js.map} +0 -0
  360. /package/dist/{public-envelope-GOG4LUM7.js.map → public-envelope-B4KNWVUA.js.map} +0 -0
  361. /package/dist/{register-ZI7YI7US.js.map → register-VAHONCID.js.map} +0 -0
  362. /package/dist/{registry-DZ2T4LL7.js.map → registry-7MLVD3VO.js.map} +0 -0
  363. /package/dist/{registry-NVWEQ2GU.js.map → registry-EPKPD5SM.js.map} +0 -0
  364. /package/dist/{registry-SEKL74ET.js.map → registry-P5T7PS2U.js.map} +0 -0
  365. /package/dist/{request-withdrawal-ZP3JIRVS.js.map → request-withdrawal-TI3A5WPS.js.map} +0 -0
  366. /package/dist/{revoke-VF6TAJK2.js.map → revoke-FDRD6GRW.js.map} +0 -0
  367. /package/dist/{seed-VI4FU7UH.js.map → seed-Q6M2IZFL.js.map} +0 -0
  368. /package/dist/{signer-DRDDCCHW.js.map → signer-MUWEIUI6.js.map} +0 -0
  369. /package/dist/{stale-TJXFQKOJ.js.map → stale-OSQ7J3AA.js.map} +0 -0
  370. /package/dist/{storage-GR7KZRH4.js.map → storage-MVFCGEQE.js.map} +0 -0
  371. /package/dist/{store-coordination-provider-5KDS2RRP.js.map → store-coordination-provider-64OEZFGS.js.map} +0 -0
  372. /package/dist/{walk-XQCBKSBW.js.map → walk-SVNDIZ62.js.map} +0 -0
  373. /package/dist/{withdraw-accessible-FMN4LPJZ.js.map → withdraw-accessible-WZIEVWSR.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/kernel/enclave/classify/verify.ts"],"sourcesContent":["/**\n * The enclave verify oracle (spec §3). Verdict-only egress: no path\n * distinguishes \"record missing\" / \"no digest\" / \"AAD-tamper\" / \"mismatch\"\n * beyond { ok: false } — existence oracles are oracles too. Every path that\n * cannot run a real comparison runs ONE dummy PBKDF2 + ONE dummy tag-compare\n * first (C4), so wall-clock cannot enumerate records/fields/answers. The\n * text door (verifyTextField) has no real PBKDF2 of its own, so it pays the\n * pad UNCONDITIONALLY — every outcome, including a real compare, costs the\n * same one PBKDF2 unit (no inverted exists-vs-absent oracle).\n * Elevated records (_tier > 0, with-audit tier moves) are non-comparable\n * through every door and pad-false exactly like missing records (#691) —\n * the tier-aware read surface is getAtTier, never verify.\n * Throws are reserved for caller/config bugs (ClassifiedVerifyError /\n * ClassifiedConfigError) and are exempt from the pad by design (R6 note).\n * @module\n */\nimport { generateSalt, base64ToBuffer, type EnclaveKey } from '../crypto.js'\nimport { pbkdf2VerifyDigest, VDIG_ITERATIONS } from './digest.js'\nimport { normalizeForVerify, type VerifyNormalizeMode } from './normalize.js'\nimport { blindedEqual } from './compare.js'\nimport { evaluateKofN } from './kofn.js'\nimport { openVdigPayload, type VdigPayload } from './vdig.js'\nimport { dualReadSealedSlot } from '../record-keys/sealed-slot.js'\nimport { ClassifiedConfigError, ClassifiedVerifyError } from '../../errors.js'\nimport type { EncryptedEnvelope, VdigFieldPolicy, ClassifiedVerdict } from '../../types.js'\n\nexport interface VerifyEngineCtx {\n readonly collection: string\n getEnvelope(id: string): Promise<EncryptedEnvelope | null>\n resolveCek(env: EncryptedEnvelope): Promise<EnclaveKey | undefined>\n getDEK(): Promise<EnclaveKey>\n readonly now: () => number\n}\n\n/**\n * C4 pad: one full-cost dummy digest + one dummy compare; result discarded.\n * Pad at `iter` when the caller holds an AUTHENTICATED payload.iter, so pad\n * cost tracks the real compare cost across future iteration bumps; the\n * fully-absent paths (no payload to read an iter from) use VDIG_ITERATIONS.\n */\nasync function padOnce(iter: number = VDIG_ITERATIONS): Promise<void> {\n const dummy = await pbkdf2VerifyDigest('noydb-classify-c4-pad', generateSalt(), iter)\n await blindedEqual(dummy, dummy)\n}\n\nasync function padFalse(): Promise<ClassifiedVerdict> {\n await padOnce()\n return { ok: false }\n}\n\n/** R6 verify-side transition evidence — config bug, fail-loud, pad-exempt. */\nfunction refuseSealedResidue(collection: string, field: string): never {\n throw new ClassifiedConfigError(collection,\n `field \"${field}\" carries a recoverable _sealed slot but no _vdig — storage-form ` +\n `transition detected (R6); refused fail-loud, never an ok:false masquerading as wrong-password`)\n}\n\nexport async function verifyDigestField(\n ctx: VerifyEngineCtx,\n id: string,\n field: string,\n candidate: string,\n policy: VdigFieldPolicy,\n): Promise<ClassifiedVerdict> {\n const env = await ctx.getEnvelope(id)\n // #691: an elevated envelope (_tier > 0) is non-comparable through every\n // tier-0 door — its key material is tier-DEK-wrapped and must never be\n // resolved here (the elevating session's warm cekCache would otherwise\n // leak past the tier audit). Exactly the missing-record pad path, checked\n // BEFORE the R6 residue probe so tier-0 callers learn nothing.\n if (env === null || (env._tier ?? 0) > 0) return padFalse()\n const blob = env._vdig?.[field]\n if (blob === undefined) {\n if (env._sealed?.[field] !== undefined) refuseSealedResidue(ctx.collection, field)\n return padFalse()\n }\n const cek = await ctx.resolveCek(env)\n if (cek === undefined) return padFalse()\n\n let payload: VdigPayload\n try {\n payload = await openVdigPayload(blob, cek, ctx.collection, id, field)\n } catch {\n return padFalse() // AAD/tamper (C1) → padded false; no tamper oracle to the caller\n }\n\n const normalized = normalizeForVerify(policy.normalize, candidate)\n const digest = await pbkdf2VerifyDigest(normalized, base64ToBuffer(payload.cur.salt), payload.iter)\n const ok = await blindedEqual(digest, base64ToBuffer(payload.cur.hash))\n if (!ok) return { ok: false } // I1: bare false — mustRotate never computed here\n\n if (policy.rotateDays !== undefined\n && ctx.now() > Date.parse(payload.cur.at) + policy.rotateDays * 86_400_000) {\n // Disclosing write-age-vs-policy to a SUCCESSFUL verifier is intended (audit F6).\n return { ok: true, mustRotate: true }\n }\n return { ok: true }\n}\n\nexport async function verifyTextField(\n ctx: VerifyEngineCtx,\n id: string,\n field: string,\n candidate: string,\n normalize: VerifyNormalizeMode,\n): Promise<ClassifiedVerdict> {\n // C4, single code path: (a) attempt the unseal where a slot exists, then\n // for EVERY outcome — present-correct, present-wrong, missing record,\n // missing slot, tampered — (b) run the one constant-cost pad, (c) run one\n // blindedEqual, (d) return the boolean. Unlike the digest door there is no\n // real PBKDF2 here (unseal is sub-ms AES-GCM), so skipping the pad on the\n // real compare would make exists-but-wrong ~100x FASTER than absent — an\n // inverted existence oracle. No branch below skips the dominant cost.\n const env = await ctx.getEnvelope(id)\n const blob = env?._sealed?.[field]\n let stored: string | undefined\n if (env !== null && (env._tier ?? 0) === 0 && blob !== undefined) { // #691: elevated ≡ missing\n const cek = await ctx.resolveCek(env)\n const dek = await ctx.getDEK()\n try {\n // Plaintext exists microseconds inside this function; only the boolean leaves.\n stored = String(JSON.parse(await dualReadSealedSlot(blob, field, ctx.collection, cek, dek)))\n } catch {\n stored = undefined // AAD/tamper (C1) → same padded compare tail as absent\n }\n }\n await padOnce()\n const a = new TextEncoder().encode(normalizeForVerify(normalize, candidate))\n const b = stored !== undefined\n ? new TextEncoder().encode(normalizeForVerify(normalize, stored))\n : a // no-compare dummy comparand — the verdict is forced false below\n const eq = await blindedEqual(a, b)\n return { ok: stored !== undefined && eq }\n}\n\nexport async function matchGroupFields(\n ctx: VerifyEngineCtx,\n id: string,\n answers: Record<string, string>,\n members: ReadonlyArray<{ readonly field: string; readonly policy: VdigFieldPolicy }>,\n opts: { readonly min: number },\n): Promise<{ readonly passed: boolean }> {\n // I2 step 1 — validate EVERYTHING up front, before any PBKDF2, throwing\n // uniformly at ~0 elapsed (no member-position leak via timing/throw type).\n if (!Number.isInteger(opts.min) || opts.min < 1 || opts.min > members.length) {\n throw new ClassifiedVerifyError(ctx.collection, '*',\n `matchGroup min ${opts.min} out of range 1..${members.length}`)\n }\n const normalized = new Map<string, string>()\n for (const m of members) {\n const answer = answers[m.field] // non-member answer keys: silently ignored\n if (answer === undefined) continue\n if (typeof answer !== 'string') {\n throw new ClassifiedVerifyError(ctx.collection, m.field, 'candidate must be a string')\n }\n normalized.set(m.field, normalizeForVerify(m.policy.normalize, answer))\n }\n\n const fetched = await ctx.getEnvelope(id)\n // #691: elevated ≡ missing — nulling the view here also skips the R6\n // residue probe (a tier-0 caller must not learn an elevated record's\n // storage form) and forces the cek-undefined pad path for every member.\n const env = fetched === null || (fetched._tier ?? 0) > 0 ? null : fetched\n if (env !== null) {\n for (const m of members) { // R6 evidence — uniform, before any PBKDF2\n if (env._sealed?.[m.field] !== undefined && env._vdig?.[m.field] === undefined) {\n refuseSealedResidue(ctx.collection, m.field)\n }\n }\n }\n const cek = env !== null ? await ctx.resolveCek(env) : undefined\n\n // I2 step 2+3 — iterate RESOLVED GROUP MEMBERS (denominator = |members|),\n // evaluate EVERY member (collect, never break), pad every no-compare slot.\n const results: boolean[] = []\n for (const m of members) {\n const candidate = normalized.get(m.field)\n const blob = env?._vdig?.[m.field]\n if (env === null || cek === undefined || blob === undefined) {\n await padOnce()\n results.push(false)\n continue\n }\n try {\n const payload = await openVdigPayload(blob, cek, ctx.collection, id, m.field)\n if (candidate === undefined) {\n // Slot present, answer absent: the payload's authenticated iter is in\n // hand — pad at IT (not the constant) so a future iteration bump\n // cannot desync this pad's cost from the real compare's cost.\n await padOnce(payload.iter)\n results.push(false)\n continue\n }\n const digest = await pbkdf2VerifyDigest(candidate, base64ToBuffer(payload.cur.salt), payload.iter)\n results.push(await blindedEqual(digest, base64ToBuffer(payload.cur.hash)))\n } catch {\n await padOnce()\n results.push(false)\n }\n }\n // Per-member results never appear in any return, error, or audit payload.\n return { passed: evaluateKofN(results, opts.min) }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwCA,eAAe,QAAQ,OAAe,iBAAgC;AACpE,QAAM,QAAQ,MAAM,mBAAmB,yBAAyB,aAAa,GAAG,IAAI;AACpF,QAAM,aAAa,OAAO,KAAK;AACjC;AAEA,eAAe,WAAuC;AACpD,QAAM,QAAQ;AACd,SAAO,EAAE,IAAI,MAAM;AACrB;AAGA,SAAS,oBAAoB,YAAoB,OAAsB;AACrE,QAAM,IAAI;AAAA,IAAsB;AAAA,IAC9B,UAAU,KAAK;AAAA,EACgF;AACnG;AAEA,eAAsB,kBACpB,KACA,IACA,OACA,WACA,QAC4B;AAC5B,QAAM,MAAM,MAAM,IAAI,YAAY,EAAE;AAMpC,MAAI,QAAQ,SAAS,IAAI,SAAS,KAAK,EAAG,QAAO,SAAS;AAC1D,QAAM,OAAO,IAAI,QAAQ,KAAK;AAC9B,MAAI,SAAS,QAAW;AACtB,QAAI,IAAI,UAAU,KAAK,MAAM,OAAW,qBAAoB,IAAI,YAAY,KAAK;AACjF,WAAO,SAAS;AAAA,EAClB;AACA,QAAM,MAAM,MAAM,IAAI,WAAW,GAAG;AACpC,MAAI,QAAQ,OAAW,QAAO,SAAS;AAEvC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,gBAAgB,MAAM,KAAK,IAAI,YAAY,IAAI,KAAK;AAAA,EACtE,QAAQ;AACN,WAAO,SAAS;AAAA,EAClB;AAEA,QAAM,aAAa,mBAAmB,OAAO,WAAW,SAAS;AACjE,QAAM,SAAS,MAAM,mBAAmB,YAAY,eAAe,QAAQ,IAAI,IAAI,GAAG,QAAQ,IAAI;AAClG,QAAM,KAAK,MAAM,aAAa,QAAQ,eAAe,QAAQ,IAAI,IAAI,CAAC;AACtE,MAAI,CAAC,GAAI,QAAO,EAAE,IAAI,MAAM;AAE5B,MAAI,OAAO,eAAe,UACrB,IAAI,IAAI,IAAI,KAAK,MAAM,QAAQ,IAAI,EAAE,IAAI,OAAO,aAAa,OAAY;AAE5E,WAAO,EAAE,IAAI,MAAM,YAAY,KAAK;AAAA,EACtC;AACA,SAAO,EAAE,IAAI,KAAK;AACpB;AAEA,eAAsB,gBACpB,KACA,IACA,OACA,WACA,WAC4B;AAQ5B,QAAM,MAAM,MAAM,IAAI,YAAY,EAAE;AACpC,QAAM,OAAO,KAAK,UAAU,KAAK;AACjC,MAAI;AACJ,MAAI,QAAQ,SAAS,IAAI,SAAS,OAAO,KAAK,SAAS,QAAW;AAChE,UAAM,MAAM,MAAM,IAAI,WAAW,GAAG;AACpC,UAAM,MAAM,MAAM,IAAI,OAAO;AAC7B,QAAI;AAEF,eAAS,OAAO,KAAK,MAAM,MAAM,mBAAmB,MAAM,OAAO,IAAI,YAAY,KAAK,GAAG,CAAC,CAAC;AAAA,IAC7F,QAAQ;AACN,eAAS;AAAA,IACX;AAAA,EACF;AACA,QAAM,QAAQ;AACd,QAAM,IAAI,IAAI,YAAY,EAAE,OAAO,mBAAmB,WAAW,SAAS,CAAC;AAC3E,QAAM,IAAI,WAAW,SACjB,IAAI,YAAY,EAAE,OAAO,mBAAmB,WAAW,MAAM,CAAC,IAC9D;AACJ,QAAM,KAAK,MAAM,aAAa,GAAG,CAAC;AAClC,SAAO,EAAE,IAAI,WAAW,UAAa,GAAG;AAC1C;AAEA,eAAsB,iBACpB,KACA,IACA,SACA,SACA,MACuC;AAGvC,MAAI,CAAC,OAAO,UAAU,KAAK,GAAG,KAAK,KAAK,MAAM,KAAK,KAAK,MAAM,QAAQ,QAAQ;AAC5E,UAAM,IAAI;AAAA,MAAsB,IAAI;AAAA,MAAY;AAAA,MAC9C,kBAAkB,KAAK,GAAG,oBAAoB,QAAQ,MAAM;AAAA,IAAE;AAAA,EAClE;AACA,QAAM,aAAa,oBAAI,IAAoB;AAC3C,aAAW,KAAK,SAAS;AACvB,UAAM,SAAS,QAAQ,EAAE,KAAK;AAC9B,QAAI,WAAW,OAAW;AAC1B,QAAI,OAAO,WAAW,UAAU;AAC9B,YAAM,IAAI,sBAAsB,IAAI,YAAY,EAAE,OAAO,4BAA4B;AAAA,IACvF;AACA,eAAW,IAAI,EAAE,OAAO,mBAAmB,EAAE,OAAO,WAAW,MAAM,CAAC;AAAA,EACxE;AAEA,QAAM,UAAU,MAAM,IAAI,YAAY,EAAE;AAIxC,QAAM,MAAM,YAAY,SAAS,QAAQ,SAAS,KAAK,IAAI,OAAO;AAClE,MAAI,QAAQ,MAAM;AAChB,eAAW,KAAK,SAAS;AACvB,UAAI,IAAI,UAAU,EAAE,KAAK,MAAM,UAAa,IAAI,QAAQ,EAAE,KAAK,MAAM,QAAW;AAC9E,4BAAoB,IAAI,YAAY,EAAE,KAAK;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAM,QAAQ,OAAO,MAAM,IAAI,WAAW,GAAG,IAAI;AAIvD,QAAM,UAAqB,CAAC;AAC5B,aAAW,KAAK,SAAS;AACvB,UAAM,YAAY,WAAW,IAAI,EAAE,KAAK;AACxC,UAAM,OAAO,KAAK,QAAQ,EAAE,KAAK;AACjC,QAAI,QAAQ,QAAQ,QAAQ,UAAa,SAAS,QAAW;AAC3D,YAAM,QAAQ;AACd,cAAQ,KAAK,KAAK;AAClB;AAAA,IACF;AACA,QAAI;AACF,YAAM,UAAU,MAAM,gBAAgB,MAAM,KAAK,IAAI,YAAY,IAAI,EAAE,KAAK;AAC5E,UAAI,cAAc,QAAW;AAI3B,cAAM,QAAQ,QAAQ,IAAI;AAC1B,gBAAQ,KAAK,KAAK;AAClB;AAAA,MACF;AACA,YAAM,SAAS,MAAM,mBAAmB,WAAW,eAAe,QAAQ,IAAI,IAAI,GAAG,QAAQ,IAAI;AACjG,cAAQ,KAAK,MAAM,aAAa,QAAQ,eAAe,QAAQ,IAAI,IAAI,CAAC,CAAC;AAAA,IAC3E,QAAQ;AACN,YAAM,QAAQ;AACd,cAAQ,KAAK,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,aAAa,SAAS,KAAK,GAAG,EAAE;AACnD;","names":[]}
@@ -3,19 +3,19 @@ import {
3
3
  } from "./chunk-VYYM3MEN.js";
4
4
  import {
5
5
  derivePersistedSchema
6
- } from "./chunk-P2LJI6JE.js";
6
+ } from "./chunk-W22IKJE2.js";
7
7
  import {
8
8
  loadPersistedSchema
9
- } from "./chunk-HK4XTXO4.js";
10
- import "./chunk-HA6ZQWA2.js";
11
- import "./chunk-J3JIYFYL.js";
12
- import "./chunk-XS6DLN67.js";
13
- import "./chunk-NM3IFKBL.js";
14
- import "./chunk-45QPICLE.js";
9
+ } from "./chunk-VZRODIM3.js";
10
+ import "./chunk-XDYREZWE.js";
11
+ import "./chunk-7DSTRQIJ.js";
12
+ import "./chunk-HB3RFQSC.js";
13
+ import "./chunk-JKJOGPZN.js";
14
+ import "./chunk-5M5DVGV7.js";
15
15
  import "./chunk-LP7BEXCT.js";
16
- import "./chunk-37WOJQWY.js";
17
- import "./chunk-JC75UBMT.js";
18
- import "./chunk-FZCGIJEJ.js";
16
+ import "./chunk-CIE2BASH.js";
17
+ import "./chunk-JNAI2HN2.js";
18
+ import "./chunk-BIOUI4HL.js";
19
19
  import "./chunk-PZ5AY32C.js";
20
20
 
21
21
  // src/with-shape/introspection/walk.ts
@@ -238,4 +238,4 @@ function summariseAggregateOp(value) {
238
238
  export {
239
239
  dumpVaultSchema
240
240
  };
241
- //# sourceMappingURL=walk-XQCBKSBW.js.map
241
+ //# sourceMappingURL=walk-SVNDIZ62.js.map
@@ -6,18 +6,18 @@ import {
6
6
  assertCanImport,
7
7
  canExport,
8
8
  canImport
9
- } from "../chunk-K2DLLK34.js";
10
- import "../chunk-CCIR2OJH.js";
11
- import "../chunk-JGUZMC3I.js";
12
- import "../chunk-HA6ZQWA2.js";
13
- import "../chunk-J3JIYFYL.js";
14
- import "../chunk-XS6DLN67.js";
15
- import "../chunk-NM3IFKBL.js";
16
- import "../chunk-45QPICLE.js";
9
+ } from "../chunk-6QTNZJBS.js";
10
+ import "../chunk-J2ZX2XUV.js";
11
+ import "../chunk-4JBYV75E.js";
12
+ import "../chunk-XDYREZWE.js";
13
+ import "../chunk-7DSTRQIJ.js";
14
+ import "../chunk-HB3RFQSC.js";
15
+ import "../chunk-JKJOGPZN.js";
16
+ import "../chunk-5M5DVGV7.js";
17
17
  import "../chunk-LP7BEXCT.js";
18
- import "../chunk-37WOJQWY.js";
19
- import "../chunk-JC75UBMT.js";
20
- import "../chunk-FZCGIJEJ.js";
18
+ import "../chunk-CIE2BASH.js";
19
+ import "../chunk-JNAI2HN2.js";
20
+ import "../chunk-BIOUI4HL.js";
21
21
  import "../chunk-PZ5AY32C.js";
22
22
  export {
23
23
  ServiceBus,
@@ -138,6 +138,13 @@ export interface ForgetResult {
138
138
  /** `collection:id:field` sealed slots that were DEK-derived (legacy, written before per-record CEKs) and thus NOT crypto-shredded
139
139
  * by dropping `_cek` — the collection DEK is retained, so synced/backup copies stay decryptable (#M-1). */
140
140
  readonly sealedResidue: readonly string[];
141
+ /** Count of `_ledger_deltas` rows hard-deleted across the shredded records (#734) — the
142
+ * erasure twin of #729's elevate-side purge. Entry metadata (that the record was mutated,
143
+ * at which version/timestamp/actor) is retained; only the plaintext delta content is removed. */
144
+ readonly ledgerDeltasPurged: number;
145
+ /** `collection:id` refs whose `_ledger_deltas` purge failed — plaintext delta residue still
146
+ * readable under the retained ledger DEK. Non-empty means erasure is INCOMPLETE. */
147
+ readonly ledgerDeltaResidue: readonly string[];
141
148
  /** The single `op:'forget'` ledger entry appended for this erasure. */
142
149
  readonly ledgerEntry: LedgerEntry;
143
150
  /** #622 — record-grain derived artifacts (MV rows, per-record derived copies) erased
@@ -162,6 +169,21 @@ export interface ForgetResult {
162
169
  * Always empty under the unconditional default. Non-empty means a `_sealed_cek` entry or a
163
170
  * blob scan was skipped for an undeclared collection — reported, never a silent skip. */
164
171
  readonly scopedPurgeResidue: readonly ScopedPurgeResidueNotice[];
172
+ /** #776/#782/#785 — `collection:id` MV-output rows that survived erasure invalidation (eager
173
+ * tombstone leg AND lazy/manual `invalidateMVAtRest`) despite belonging to the forgotten
174
+ * subject, because the `_materializedFrom` ownership stamp could NOT be decoded (undecodable
175
+ * under the collection's default DEK — e.g. elevated above tier 0 on a tiered output
176
+ * collection; ownership unconfirmed — could be a plain user record on a same-collection
177
+ * partition MV). Never erased, but surfaced here rather than silently skipped (the #724
178
+ * posture). Non-empty means the row may still hold the forgotten/pre-elevation contribution,
179
+ * decryptable by tier-holders. */
180
+ readonly derivedResidueUndecodable: readonly string[];
181
+ /** #782/#785 — `collection:id` MV-output rows that DID decode and stamp-match via
182
+ * `_materializedFrom`, but whose erasure was declined by the #718 tier-elevation gate
183
+ * (`_internalDelete` returned false). Ownership CONFIRMED here — a real silent survival,
184
+ * not a stamp-mismatch skip — surfaced rather than dropped. Non-empty means a live,
185
+ * tier-holder-decryptable copy of the forgotten contribution was deliberately retained. */
186
+ readonly derivedResidueDeclined: readonly string[];
165
187
  }
166
188
  /** #633 — the two `scopedPurgeResidue` skip reasons. Single source of truth: `purge-scope.ts`
167
189
  * (the port-internal partition helpers) imports this rather than redeclaring it. */
@@ -26,6 +26,7 @@ export { TiersNotEnabledError } from '../../kernel/errors.js';
26
26
  import { type RecordCodec, type EnclaveKey, type SealedShredSlot } from '../../kernel/enclave/index.js';
27
27
  import type { UnlockedKeyring } from '../../with-party/team/keyring.js';
28
28
  import type { Lru } from '../../kernel/cache/index.js';
29
+ import type { BlobFieldsConfig } from '../../with-shape/blobs/blob-compaction.js';
29
30
  import { type NoydbStore, type EncryptedEnvelope, type GhostRecord, type TierMode, type CrossTierAccessEvent } from '../../kernel/types.js';
30
31
  /** Everything the moving tier methods touched on `this.*`, as a flat context. */
31
32
  export interface TiersContext<T> {
@@ -46,6 +47,168 @@ export interface TiersContext<T> {
46
47
  * owns. `null` → no caching.
47
48
  */
48
49
  readonly cekCache: Lru<string, EnclaveKey> | null;
50
+ /**
51
+ * Sync the collection's decoded-record cache after a tier move rewraps the
52
+ * envelope (#691). The lazy LRU is evicted on EVERY call — it is never
53
+ * re-seeded, since a lazy `#getRaw` miss refetches and decodes the fresh
54
+ * envelope via its adapter fallback (a stale LRU entry after a tier-0
55
+ * `putAtTier` overwrite was the #702 lazy-mode gap). `null` → also evict
56
+ * the eager cache entry; an `entry` → set the eager cache to the given
57
+ * decoded record/version (used by `demote()` landing back at tier 0 and
58
+ * `putAtTier`'s tier-0 write, #702 — the record is tier-0, so it must stay
59
+ * plain-`get()`-readable in-session).
60
+ */
61
+ syncCache(id: string, entry: {
62
+ record: T;
63
+ version: number;
64
+ } | null): void;
65
+ /**
66
+ * Sync the collection's indexes after a tier move rewraps the envelope
67
+ * (#709). Persisted `_idx/<field>/<recordId>` side-cars hold the indexed
68
+ * field's PLAINTEXT value and are always encrypted under the tier-0 DEK,
69
+ * whatever the record's own tier — the same leak class `forget()` fixed
70
+ * via `purgePersistedIndexes` ("forget() crypto-shreds the body but keeps
71
+ * the collection DEK, under which these side-cars are encrypted — so
72
+ * without this they leave the indexed field VALUES readable"). `null` →
73
+ * the record just left tier 0: purge its persisted side-cars and drop its
74
+ * in-memory index entries. A record → it is tier-0 again: (re)build its
75
+ * entries from that record. Call this BEFORE {@link syncCache} — the
76
+ * implementation reads the pre-write cached value as "previous" to clean
77
+ * up stale index buckets, so it must run while that entry is still live.
78
+ * `version` stamps a rebuilt side-car's own envelope version — every
79
+ * caller has one in scope (the envelope it just wrote), including the
80
+ * purge (`null`) branches, which pass it along even though it's ignored
81
+ * there (#720: keeps the parameter honestly required, no dead default).
82
+ * `priorEnvelope` (#720) — the RAW envelope read BEFORE this call's own
83
+ * overwrite (already in scope as `existing`/`envelope` at every record-
84
+ * branch call site), so a lazy same-tier value change (a `putAtTier(0)`
85
+ * dropping an indexed field, a `demote(0)` off an elevated record) can
86
+ * still be tier-gate-decoded as "previous" once the live envelope itself
87
+ * has already moved past it. The `null`-record branches never pass one —
88
+ * see the implementation's doc comment for why they don't need to.
89
+ */
90
+ syncIndexes(id: string, record: T | null, version: number, priorEnvelope?: EncryptedEnvelope): Promise<void>;
91
+ /** Sync the collection's SEARCH artifacts after a tier move (#721). Both the
92
+ * lexical `_ftindex` blob and the `_vec/<id>` embedding are encrypted under
93
+ * the tier-0 DEK and hold the record's derived plaintext (full field text /
94
+ * a text-invertible vector), so leaving them means elevation never hid what
95
+ * the record was searchable by — the `forget()` precedent, unapplied to
96
+ * elevate. `null` → the record left tier 0: purge its `_vec`, and
97
+ * invalidate the `_ftindex` blob (the cache-driven rebuild then excludes
98
+ * it). A record → it is tier-0 again: re-embed it, and invalidate
99
+ * `_ftindex` (rebuild includes it). No-op fast when the collection has no
100
+ * search. */
101
+ syncSearch(id: string, record: T | null, version?: number): Promise<void>;
102
+ /**
103
+ * Sync a record's `_history` snapshots after a tier move rewraps the live
104
+ * envelope (#712, at-rest hardening). Each snapshot carries its own key
105
+ * material (a direct DEK wrap, or a per-record `_cek` itself wrapped under
106
+ * the tier DEK) — `elevate()`'s live-body rewrap moves only the live
107
+ * envelope, leaving prior versions decryptable at rest under the tier the
108
+ * record has moved away from (the same leak class `forget()`'s
109
+ * `tombstoneHistory` closed for erasure). `fromDek`/`toDek` are the SAME
110
+ * DEKs the caller already resolved for the live rewrap — reuse them, don't
111
+ * recompute. No-op when the strategy is `NO_HISTORY`.
112
+ */
113
+ syncHistory(id: string, fromDek: EnclaveKey, toDek: EnclaveKey): Promise<void>;
114
+ /**
115
+ * Save a raw pre-move envelope as a `_history` snapshot (#728). Tier moves
116
+ * (`putAtTier`/`elevate`/`demote`) bump `_v` and overwrite the live
117
+ * envelope without ever snapshotting the version that existed just before
118
+ * the move — so `history()` silently lost it. The caller (`tiers/index.ts`)
119
+ * builds `envelope` by reusing the SAME `rewrapBodyToDek(envelope, fromDek,
120
+ * toDek)` result it already computed for the live write, so the snapshot
121
+ * lands wrapped under the DESTINATION tier's DEK — never `ctx.codec.
122
+ * encryptRecord`, which always resolves the tier-0 DEK and would leak the
123
+ * pre-move body whenever `fromTier > 0`. No-op when history is disabled
124
+ * (folds the `historyConfig.enabled` gate so `tiers/index.ts` stays simple).
125
+ */
126
+ saveHistorySnapshot(id: string, envelope: EncryptedEnvelope): Promise<void>;
127
+ /**
128
+ * `true` iff a real history strategy is wired (not `NO_HISTORY`) AND not
129
+ * explicitly disabled via `historyConfig.enabled`. `putAtTier` checks this
130
+ * BEFORE decrypting `existing` to build a snapshot (#728, #737 regression
131
+ * fix) — unlike elevate/demote, which reuse a `body` rewrap their live
132
+ * write needs unconditionally, putAtTier's live write never decrypts the
133
+ * prior envelope, so building a snapshot is the ONLY reason it would
134
+ * touch (and potentially throw decrypting) `existing`'s ciphertext. A
135
+ * derivation-free, history-disabled collection must not pay that decode —
136
+ * or risk it, on a corrupted/inaccessible prior body — same law
137
+ * `hasDerivedOutputs` already enforces for the derived-outputs decode.
138
+ */
139
+ readonly historyEnabled: boolean;
140
+ /**
141
+ * Rehome a record's blob attachments after a tier move (#724 Arc 10 Task
142
+ * 2, at-rest hardening). A blob's home tier is its owning record's tier —
143
+ * the `_blob` DEK is tier-scoped (`dekKey('_blob', tier)`), so a solo-
144
+ * owned blob's content CEK must move with the record or it stays
145
+ * tier-0-unwrappable at rest even once the live body has moved (the same
146
+ * leak class `syncHistory` closes for `_history` snapshots). Delegates to
147
+ * `BlobSet.rehomeForTier`; no-op fast when the collection has no blob
148
+ * fields. Order-independent with the other sync hooks — touches only
149
+ * blob side structures, not this collection's own cache/indexes.
150
+ */
151
+ syncBlobs(id: string, fromTier: number, toTier: number): Promise<void>;
152
+ /**
153
+ * Purge a record's tier-0-era plaintext ledger deltas after a tier move
154
+ * lands it above tier 0 (#729). The audit ledger is a flat, vault-wide,
155
+ * hash-chained log — a record's reverse-JSON-Patch deltas in
156
+ * `_ledger_deltas` are encrypted under one collection-wide `_ledger` DEK
157
+ * (not the record's own tier DEK), so `elevate()`/`putAtTier()`'s live-body
158
+ * rewrap never moves them: they stay readable at rest to any tier-0
159
+ * caller, the same leak class `syncHistory` closed for `_history`
160
+ * snapshots. Chain-safe: `verify()` reads only `_ledger` entry fields
161
+ * (including `deltaHash`, which lives on the entry, not the deleted delta
162
+ * row), never `_ledger_deltas`, so a purge cannot break it.
163
+ * **Irreversible** — the deleted plaintext cannot be restored by
164
+ * `demote()`. **Metadata-retained** — the `_ledger` entries (that the
165
+ * record was mutated, at which version/timestamp/actor) are untouched;
166
+ * only the delta *content* is purged. No-op when the collection has no
167
+ * ledger (`withHistory()` not enabled).
168
+ */
169
+ syncLedger(id: string): Promise<void>;
170
+ /**
171
+ * Sync a record's derived outputs (materialized-view rows, rollup
172
+ * contributions, `withDerivation` outputs) after a tier move (#722).
173
+ * Those outputs are computed from this record and written to OTHER
174
+ * (output) collections via a plain `put` at tier 0 — `elevate()`'s live-
175
+ * body rewrap moves only the source envelope, so the output rows keep
176
+ * holding the source's tier-0-era plaintext, the same leak class
177
+ * `syncSearch`/`syncIndexes` closed for THIS collection's own artifacts.
178
+ * `elevated` (landing tier > 0) reuses the SAME onDelete fanout
179
+ * `forgetDerivedFanout` drives for `forget()` (`kernel/via/dispatch.ts`)
180
+ * — minus its `'ref'` cascade edge (elevate/demote never erase a
181
+ * *different* record) — to recompute this record's derived outputs as
182
+ * REMOVED. Recompute is tier-safe: the fanout's own source scan reads
183
+ * the elevated-excluding cache (#701/#709/#712), so it naturally omits
184
+ * the now-elevated record instead of re-embedding its plaintext. Landing
185
+ * back at tier 0 (`elevated === false`) is the reverse (Task 2, #722):
186
+ * `syncDerivedOutputs` instead runs the ordinary local-write add-
187
+ * dispatchers (`dispatchDerivations`/`dispatchMaterializedViews`, the
188
+ * same ones a plain `put()` fires) to restore the contribution —
189
+ * reversible, since the source's plaintext survives the elevate/demote
190
+ * rewrap round-trip. `record` is the decoded source record (PRE-move on
191
+ * the remove side, POST-move on the add side); only the rollup edge
192
+ * consumes it on remove, both add-dispatchers require it on add; `null`
193
+ * (tombstone/delete-marker) skips both. `version` stamps the add-
194
+ * dispatchers' `_derivedFrom.sourceVersion` metadata — reuses the version
195
+ * the tier op already has, no re-read; unused on the remove side. No-op
196
+ * fast when the collection has no MV/derivation source — each dispatcher
197
+ * below checks its own source before doing any work.
198
+ */
199
+ syncDerived(id: string, record: T | null, elevated: boolean, version?: number): Promise<void>;
200
+ /**
201
+ * `true` iff the collection has a materialized-view or derivation source
202
+ * attached. Gates the {@link syncDerived} pre-move decode on the remove
203
+ * direction (`elevate()`, `putAtTier(tier>0)`, `demote()`'s intermediate-
204
+ * tier branch): those sites decode the record SOLELY to feed
205
+ * `syncDerived(..., true)`, whose dispatchers no-op immediately when the
206
+ * collection has no MV/derivation source — so a no-derivation collection
207
+ * would otherwise pay a full record-body decrypt on every tier move for
208
+ * nothing (#722 perf regression). `false` short-circuits the decode to
209
+ * `null`, which `syncDerivedOutputs` already treats safely.
210
+ */
211
+ readonly hasDerivedOutputs: boolean;
49
212
  /** Emit `_source`/`_sourceTs` provenance fields when a source is supplied. */
50
213
  readonly provenance: boolean;
51
214
  /** Declared tiers, or null when the feature is off. */
@@ -56,9 +219,108 @@ export interface TiersContext<T> {
56
219
  getDEK(key: string): Promise<EnclaveKey>;
57
220
  /** Fire a cross-tier access event (sink stays collection-resident). */
58
221
  emitCrossTierEvent(event: CrossTierAccessEvent): void;
222
+ /**
223
+ * Register this record's ref in the encrypted `_subject_index` (#766).
224
+ * `putAtTier`'s raw `ctx.adapter.put` bypasses `Collection.put()`'s write-
225
+ * hook pipeline — the `onAfterWrite` hook that normally does this — so a
226
+ * record whose FIRST persistence is `putAtTier` (sensitive from birth, a
227
+ * documented legitimate use) would otherwise never enter the index and
228
+ * stay unreachable by `vault.forget()`. Idempotent (safe on every call,
229
+ * including a repeat write of an already-registered record) and a no-op
230
+ * when the collection declares no forget-subject field.
231
+ */
232
+ addSubjectRef(id: string, record: T): Promise<void>;
59
233
  }
60
234
  export declare function assertTiersEnabled<T>(ctx: TiersContext<T>): void;
61
235
  export declare function assertDeclaredTier<T>(ctx: TiersContext<T>, tier: number): void;
236
+ /**
237
+ * Tier-composition guard (#724 / Arc 7 of the tier-invisibility campaign).
238
+ *
239
+ * Refuses `tiers` declared together with a derived-artifact feature whose
240
+ * crypto has not yet been made tier-aware — i.e. a feature `elevate()` /
241
+ * `demote()` does not re-key when a record moves tiers, so an elevated
242
+ * record's data for that feature would stay readable at tier 0.
243
+ *
244
+ * **Status:** the original call site (`Collection` constructor, beside
245
+ * `buildUniqueConstraintSet`) was removed by Arc 10 Task 1 (#724) — the
246
+ * `tiers + blobFields` refusal below was superseded by a runtime read gate
247
+ * on `collection.blob(id)` (`with-shape/blobs/blob-set.ts`), so this
248
+ * function currently has no caller. Relocated here from
249
+ * `with-lookup/indexing/unique-constraints.ts` (#733) so the tier-domain
250
+ * guard lives in the tier domain, rather than an indexing file it only
251
+ * borrowed for a grandfathered import specifier.
252
+ *
253
+ * ## #724 verified: `tiers + blobFields` leaks
254
+ *
255
+ * `collection.blob(id)` (`Collection.blob()`) never checks the live
256
+ * record's tier before returning a `BlobSet` handle, and `BlobSet`'s crypto
257
+ * is entirely orthogonal to the tier ladder:
258
+ * - the slot map (`_blob_slots_{collection}/{id}`) is encrypted under the
259
+ * collection's TIER-0 DEK (`getDEK(name)` ≡ `dekKey(name, 0)` — see
260
+ * `dekKey` in `with-party/team/tiers.ts`), never a tier-N DEK;
261
+ * - chunk content (`_blob_index` / `_blob_chunks`) is encrypted under the
262
+ * vault-shared `_blob` DEK (`BLOB_COLLECTION`), which has no tier
263
+ * dimension at all.
264
+ * `elevate()`/`demote()` (this file) rewrap only the live record body,
265
+ * `_history` snapshots (#712), and index side-cars — blob slots and chunks
266
+ * are untouched. A caller whose keyring never held the record's elevated
267
+ * tier DEK can still open `collection.blob(id).get(slot)` and read full
268
+ * plaintext, even though `collection.get(id)` correctly reports the same
269
+ * record as invisible. See `__tests__/tier-composition-guard.test.ts` for
270
+ * the reproduction.
271
+ *
272
+ * ## Safe today — no check needed here
273
+ *
274
+ * Field indexes, search (`textIndexes`/`embeddings`), `withHistory`
275
+ * (snapshot keys are rewrapped by `elevate()`/`demote()`, #712), and the
276
+ * decoded-record cache (evicted on tier moves) are already tier-safe. They
277
+ * are not enumerated below; only known-unsafe features are refused, so any
278
+ * combination not named here passes silently by default.
279
+ *
280
+ * ## Deliberately NOT handled here
281
+ *
282
+ * - `ledger` — `withHistory` threads a ledger writer onto every tiered
283
+ * collection by default in shipped usage; refusing `tiers + ledger` would
284
+ * break that default. A ledger-specific tier-rekey handler (rewrap ledger
285
+ * entries, or scope ledger visibility to tier) is a separate arc.
286
+ * - materialized-view (MV) output — an MV's fanout spec lives on the
287
+ * SOURCE collection, not on the MV collection's own config, so it is not
288
+ * visible at `vault.collection()` registration time and this guard cannot
289
+ * detect it structurally.
290
+ */
291
+ export declare function assertTierComposition(collectionName: string, cfg: {
292
+ readonly tiers: boolean;
293
+ readonly blobFields: BlobFieldsConfig | undefined;
294
+ }): void;
295
+ /**
296
+ * Structural surface {@link syncDerivedOutputs} needs from the owning
297
+ * collection — the SAME onDelete dispatchers `forgetDerivedFanout` already
298
+ * drives (`kernel/via/dispatch.ts`), reused as-is (#722). Narrower than
299
+ * `Collection<T>` (avoids an import cycle: collection.ts constructs
300
+ * `TiersContext` FROM `this`, which already satisfies this shape
301
+ * structurally — no cast needed at the `tiersContext()` call site).
302
+ */
303
+ export interface DerivedOutputsHost<T> {
304
+ dispatchMaterializedViewsOnDelete(id: string): Promise<{
305
+ deleted: number;
306
+ residueUndecodable: string[];
307
+ residueDeclined: string[];
308
+ }>;
309
+ dispatchArrayDerivationsOnDelete(id: string, eraseRecordShapeToo?: boolean): Promise<number>;
310
+ dispatchRollupsOnDelete(id: string, deleted: T): Promise<unknown>;
311
+ /** Task 2 (#722) add-direction: the same local-write dispatchers an ordinary `put()` fires. */
312
+ dispatchMaterializedViews(id: string, record: T): Promise<void>;
313
+ dispatchDerivations(id: string, record: T, version: number): Promise<void>;
314
+ }
315
+ /**
316
+ * The {@link TiersContext.syncDerived} implementation collection.ts's
317
+ * `tiersContext()` binds `this` into (#722). See that field's doc comment
318
+ * for the design; `host` is `this` — every dispatcher below is a public
319
+ * Collection method, already self-guarding (no-op) when the collection has
320
+ * no materialized-view/derivation source, so no separate fast-path check is
321
+ * needed here.
322
+ */
323
+ export declare function syncDerivedOutputs<T>(host: DerivedOutputsHost<T>, id: string, record: T | null, elevated: boolean, version?: number): Promise<void>;
62
324
  /**
63
325
  * tier-aware put. Encrypts the record with the collection's tier-N DEK and
64
326
  * stamps `_tier: N` on the envelope. The caller's keyring must hold the tier-N
@@ -78,7 +340,7 @@ export declare function putAtTier<T>(ctx: TiersContext<T>, id: string, record: T
78
340
  };
79
341
  source?: string;
80
342
  sourceTs?: string;
81
- }): Promise<void>;
343
+ }): Promise<TierMoveResult>;
82
344
  /**
83
345
  * tier-aware get. When the stored record is at a tier the caller cannot
84
346
  * decrypt:
@@ -100,18 +362,28 @@ export declare function listAtTier<T>(ctx: TiersContext<T>): Promise<Array<{
100
362
  tier: number;
101
363
  readable: boolean;
102
364
  }>>;
365
+ /**
366
+ * Result of a completed `elevate()`/`demote()` (#764). The record move
367
+ * (put + indexes/cache/ledger/derived/history/blobs sync) always completes —
368
+ * `searchResidue: true` means only the search-index side (`_vec`/`_ftindex`)
369
+ * was left in a needs-retry state (a stuck `PersistedIndexCompensationError`),
370
+ * mirroring `forget()`'s `indexResidue` posture rather than aborting the move.
371
+ */
372
+ export interface TierMoveResult {
373
+ readonly searchResidue: boolean;
374
+ }
103
375
  /**
104
376
  * elevate a record to a higher tier. Re-encrypts with the target tier's DEK.
105
377
  * The caller must hold DEKs for both the current tier (to decrypt) and the
106
378
  * target tier (to re-encrypt). Stamps `_elevatedBy` with the caller id so
107
379
  * `demote()` can check the reverse operation.
108
380
  */
109
- export declare function elevate<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<void>;
381
+ export declare function elevate<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<TierMoveResult>;
110
382
  /**
111
383
  * demote a record to a lower tier. Allowed only for the user who performed the
112
384
  * last elevation or an owner.
113
385
  */
114
- export declare function demote<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<void>;
386
+ export declare function demote<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<TierMoveResult>;
115
387
  /**
116
388
  * Classify a live envelope's `_sealed` slots for crypto-shred completeness
117
389
  * (#M-1). Thin delegate to {@link RecordCodec.classifySealedShred}; surfaced
@@ -8,7 +8,8 @@
8
8
  * @internal
9
9
  */
10
10
  import type { GhostRecord } from '../../kernel/types.js';
11
- import type { TiersContext } from './index.js';
11
+ import type { TiersContext, TierMoveResult } from './index.js';
12
+ export type { TierMoveResult };
12
13
  export interface TiersStrategy {
13
14
  putAtTier<T>(ctx: TiersContext<T>, id: string, record: T, tier: number, opts?: {
14
15
  elevation?: {
@@ -17,15 +18,15 @@ export interface TiersStrategy {
17
18
  };
18
19
  source?: string;
19
20
  sourceTs?: string;
20
- }): Promise<void>;
21
+ }): Promise<TierMoveResult>;
21
22
  getAtTier<T>(ctx: TiersContext<T>, id: string): Promise<T | GhostRecord | null>;
22
23
  listAtTier<T>(ctx: TiersContext<T>): Promise<Array<{
23
24
  id: string;
24
25
  tier: number;
25
26
  readable: boolean;
26
27
  }>>;
27
- elevate<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<void>;
28
- demote<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<void>;
28
+ elevate<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<TierMoveResult>;
29
+ demote<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<TierMoveResult>;
29
30
  }
30
31
  /**
31
32
  * No-op stub — the floor default. Every capability method throws
@@ -7,7 +7,7 @@
7
7
  * @module
8
8
  */
9
9
  import type { Vault } from '../kernel/vault.js';
10
- import { type WalkClosureOptions } from './walk-closure.js';
10
+ import { type WalkClosureOptions, type DanglingRefNotice } from './walk-closure.js';
11
11
  export interface ExtractionPreview {
12
12
  readonly totalRecords: number;
13
13
  /** Sum of serialized encrypted-envelope sizes (bytes). */
@@ -29,5 +29,13 @@ export interface ExtractionPreview {
29
29
  readonly collection: string;
30
30
  readonly id: string;
31
31
  }>;
32
+ /**
33
+ * #772: outbound FK edges whose referenced parent was excluded from the
34
+ * closure (missing, or tier-elevated and therefore invisible) — mirrors
35
+ * `ExtractPartitionResult.danglingRefs` (#759) so a preview/extract pair
36
+ * gives the SAME signal. A parent excluded this way never appears in
37
+ * `byCollection` at all; this is the only preview-time indicator.
38
+ */
39
+ readonly danglingRefs: ReadonlyArray<DanglingRefNotice>;
32
40
  }
33
41
  export declare function describeExtraction(vault: Vault, opts: WalkClosureOptions): Promise<ExtractionPreview>;
@@ -9,7 +9,7 @@
9
9
  import type { Vault } from '../kernel/vault.js';
10
10
  import type { EncryptedEnvelope } from '../kernel/types.js';
11
11
  import { type EnclaveKey } from '../kernel/enclave/index.js';
12
- import { type WalkClosureOptions } from './walk-closure.js';
12
+ import { type WalkClosureOptions, type DanglingRefNotice } from './walk-closure.js';
13
13
  import { type TransferSealPayload } from '../with-pod/bundle.js';
14
14
  /** Re-keyed collections snapshot + the fresh DEKs used. */
15
15
  export interface ReKeyResult {
@@ -113,6 +113,14 @@ export interface ExtractPartitionResult {
113
113
  /** Raw 32-byte transfer key — deliver out-of-band; required to adopt. */
114
114
  readonly transferKey: Uint8Array;
115
115
  readonly sealId: string;
116
+ /**
117
+ * #759: outbound FK edges whose referenced parent was excluded from the
118
+ * partition (missing, or tier-elevated and therefore invisible). The
119
+ * child record still carries its FK value; the parent did not travel —
120
+ * callers should surface this to the operator rather than assume
121
+ * referential completeness.
122
+ */
123
+ readonly danglingRefs: ReadonlyArray<DanglingRefNotice>;
116
124
  }
117
125
  /**
118
126
  * Extract a re-keyed, transfer-sealed partition. Owner-only
@@ -8,9 +8,10 @@
8
8
  * 1. INBOUND expansion: from selected records, pull every record
9
9
  * that references them (children travel with parents), to a
10
10
  * fixed point.
11
- * 2. OUTBOUND completion: pull every parent the selected set
12
- * references (no dangling FKs), transitively, WITHOUT
13
- * re-expanding inbound from those parents (bounds the closure).
11
+ * 2. OUTBOUND completion: pull every VISIBLE parent the selected set
12
+ * references, transitively, WITHOUT re-expanding inbound from those
13
+ * parents (bounds the closure). A tier-elevated (or missing) parent is
14
+ * excluded rather than admitted — see `danglingRefs` on the result.
14
15
  *
15
16
  * The FK graph is auto-derived from the vault's existing RefRegistry
16
17
  * (the `ref('target')` declarations on collections) — no hand-written
@@ -25,6 +26,32 @@ export interface WalkClosureOptions {
25
26
  /** Max fixed-point iterations before throwing. Default 16. */
26
27
  readonly maxDepth?: number;
27
28
  }
29
+ /**
30
+ * #759: an outbound FK edge whose referenced parent was excluded from the
31
+ * closure — either because it doesn't exist, or because it is tier-elevated
32
+ * and therefore invisible (same "elevated ≡ missing" semantics as root
33
+ * selection / inbound expansion). The child keeps its FK value; the parent
34
+ * does not travel. Callers (extract-partition) surface this as a residue
35
+ * notice rather than silently dropping it.
36
+ */
37
+ export interface DanglingRefNotice {
38
+ /** Collection of the child record that holds the dangling FK. */
39
+ readonly collection: string;
40
+ /** Id of the child record. */
41
+ readonly id: string;
42
+ /** FK field on the child that references the missing/elevated parent. */
43
+ readonly field: string;
44
+ /** Target collection the FK points at. */
45
+ readonly target: string;
46
+ /** Id of the missing/elevated parent. */
47
+ readonly targetId: string;
48
+ /**
49
+ * #772: distinguishes an intentional tier boundary (`'elevated'` — the
50
+ * parent exists but is above the caller's readable tier) from a genuine
51
+ * data-integrity gap (`'missing'` — no envelope for `targetId` at all).
52
+ */
53
+ readonly reason: 'missing' | 'elevated';
54
+ }
28
55
  export interface ClosureResult {
29
56
  /** collection → set of record ids that travel together. */
30
57
  readonly closure: Map<string, Set<string>>;
@@ -34,5 +61,7 @@ export interface ClosureResult {
34
61
  /** True if an edge pointed back to an already-selected node. */
35
62
  readonly cyclesDetected: boolean;
36
63
  };
64
+ /** #759: outbound FK edges whose parent was excluded (missing or elevated). */
65
+ readonly danglingRefs: ReadonlyArray<DanglingRefNotice>;
37
66
  }
38
67
  export declare function walkClosure(vault: Vault, opts: WalkClosureOptions): Promise<ClosureResult>;
@@ -1,4 +1,5 @@
1
1
  import type { NoydbStore, EncryptedEnvelope, HistoryOptions, PruneOptions } from '../../kernel/types.js';
2
+ import { type EnclaveKey } from '../../kernel/enclave/index.js';
2
3
  /** Save a history entry (a complete encrypted envelope snapshot). */
3
4
  export declare function saveHistory(adapter: NoydbStore, vault: string, collection: string, recordId: string, envelope: EncryptedEnvelope): Promise<void>;
4
5
  /** Get history entries for a record, sorted newest-first. */
@@ -25,3 +26,48 @@ export declare function clearHistory(adapter: NoydbStore, vault: string, collect
25
26
  * Returns the number of history versions newly tombstoned.
26
27
  */
27
28
  export declare function tombstoneHistory(adapter: NoydbStore, vault: string, collection: string, recordId: string, actor: string, encrypted: boolean): Promise<number>;
29
+ /**
30
+ * Re-key every `_history` snapshot of a record from `fromDek` to `toDek`.
31
+ * Mirrors what `rewrapBodyToDek` already does for a record's LIVE body on a
32
+ * tier move (elevate/demote/putAtTier) — each `_history` envelope also
33
+ * carries its own `_cek`, wrapped under the collection's tier-0 DEK at write
34
+ * time (`record-codec.ts`), so a tier move that rewraps only the live
35
+ * envelope leaves prior versions decryptable at rest under the tier the
36
+ * record left. This is defense-in-depth *beneath* the read-gate
37
+ * (`history()`/`getVersion()` already return empty for an elevated record;
38
+ * this protects the ciphertext even if that gate is bypassed).
39
+ *
40
+ * Rewraps content in place — unlike `tombstoneHistory`, it does NOT blank
41
+ * `_iv`/`_data`/`_cek` — so a subsequent `demote()` restores tier-0
42
+ * readability. Tombstone-shaped entries (a forgotten/shredded version —
43
+ * blanked `_data`, no `_cek`) are skipped: there is no key material left to
44
+ * rewrap.
45
+ *
46
+ * **Legacy fallback.** A snapshot written before this fix stays wrapped
47
+ * under the tier-0 DEK even after its live record has since moved tiers, so
48
+ * a rewrap attempted with a tier-N `fromDek` fails to unwrap/decrypt. When
49
+ * the caller supplies `tier0Dek`, a failed rewrap is retried once with
50
+ * `tier0Dek` as `fromDek` (the only other key a pre-fix snapshot can be
51
+ * wrapped under — history is written only by tier-0 `put()`). The output is
52
+ * always wrapped under `toDek` regardless of which `fromDek` succeeded. A
53
+ * rewrap that fails under BOTH keys re-throws — that is real corruption, not
54
+ * a tier mismatch, and must not be swallowed.
55
+ *
56
+ * **Crash-atomicity / idempotency (#712 whole-branch-fix-3).** This loop has
57
+ * no transaction around it: a crash after some entries have been rewritten
58
+ * under `toDek` but before the loop finishes leaves the record's history
59
+ * split across two keys. A retry of the SAME call must not re-fail on the
60
+ * entries that already made it — so each entry is probed with
61
+ * `isRewrappedUnder(env, toDek)` FIRST; a match means it's already at the
62
+ * target key and is skipped (put nothing). This makes same-target retries
63
+ * and demote-after-crash fully self-healing. It does NOT close every crash
64
+ * window: a crash that lands SOME entries under `toDek` while the record's
65
+ * NEXT move target differs from this call's `toDek` (an intermediate-tier
66
+ * crash — e.g. elevate 0→1 crashes mid-loop, then the record is moved 1→2)
67
+ * still finds those entries unreadable under either `fromDek` or the
68
+ * tier-0 fallback, since `toDek` is a third key the next call never probes
69
+ * for. That residual window is an accepted, fail-closed limitation (see
70
+ * `.changeset/history-at-rest.md` and the design doc) — availability is
71
+ * lost, never confidentiality.
72
+ */
73
+ export declare function rewrapHistory(adapter: NoydbStore, vault: string, collection: string, recordId: string, fromDek: EnclaveKey, toDek: EnclaveKey, tier0Dek?: EnclaveKey): Promise<void>;