@noy-db/hub 0.6.0-pre.9 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/CHANGELOG.md +7079 -0
  2. package/codemods/0.6.0-pre.json +211 -0
  3. package/dist/api-6X2VLZ7J.js +20 -0
  4. package/dist/attestation/index.js +19 -19
  5. package/dist/{backup-KULFZ2T4.js → backup-VL4GRIEW.js} +36 -18
  6. package/dist/backup-VL4GRIEW.js.map +1 -0
  7. package/dist/blobs/index.js +22 -20
  8. package/dist/blobs/index.js.map +1 -1
  9. package/dist/broker/index.js +12 -12
  10. package/dist/cargo/index.js +35 -35
  11. package/dist/{chunk-KUDZVJKQ.js → chunk-22GR3O4J.js} +2 -2
  12. package/dist/{chunk-CCZJJ4I2.js → chunk-2CBFYXJX.js} +6 -6
  13. package/dist/{chunk-JJNOLSG5.js → chunk-2J5IT37K.js} +15 -16
  14. package/dist/chunk-2J5IT37K.js.map +1 -0
  15. package/dist/{chunk-PNKHAKQ7.js → chunk-2ZCWHCLF.js} +17 -10
  16. package/dist/chunk-2ZCWHCLF.js.map +1 -0
  17. package/dist/{chunk-2BMRJ6LQ.js → chunk-2ZT4INZ4.js} +8 -11
  18. package/dist/{chunk-2BMRJ6LQ.js.map → chunk-2ZT4INZ4.js.map} +1 -1
  19. package/dist/{chunk-FAXZQ5UF.js → chunk-323MN67Z.js} +2 -2
  20. package/dist/{chunk-VMCCGUQB.js → chunk-36CEG6CM.js} +2 -2
  21. package/dist/{chunk-PDLLCOGD.js → chunk-3OE2QNSM.js} +20 -17
  22. package/dist/chunk-3OE2QNSM.js.map +1 -0
  23. package/dist/{chunk-AHLJSVRL.js → chunk-3YGUHUJA.js} +5 -7
  24. package/dist/chunk-3YGUHUJA.js.map +1 -0
  25. package/dist/{chunk-ZJADGNYF.js → chunk-4AOERLTV.js} +1 -1
  26. package/dist/chunk-4AOERLTV.js.map +1 -0
  27. package/dist/{chunk-BRVW2GS5.js → chunk-4ATF4BLF.js} +87 -34
  28. package/dist/chunk-4ATF4BLF.js.map +1 -0
  29. package/dist/{chunk-747LRSOO.js → chunk-4K6BIMLF.js} +2 -2
  30. package/dist/{chunk-5O5HRRTV.js → chunk-5ATC6UT4.js} +7 -7
  31. package/dist/{chunk-5O5HRRTV.js.map → chunk-5ATC6UT4.js.map} +1 -1
  32. package/dist/{chunk-4GZ6XOVW.js → chunk-5F7BZAGV.js} +44 -42
  33. package/dist/chunk-5F7BZAGV.js.map +1 -0
  34. package/dist/{chunk-FNSMZQS4.js → chunk-5IHGJHWC.js} +2 -2
  35. package/dist/{chunk-4I72F5DF.js → chunk-5LZZQMIC.js} +11 -14
  36. package/dist/chunk-5LZZQMIC.js.map +1 -0
  37. package/dist/{chunk-HPLWFNG4.js → chunk-5O5ZSW7N.js} +2 -2
  38. package/dist/{chunk-CEJWMBXO.js → chunk-5OT4G5N5.js} +6 -2
  39. package/dist/chunk-5OT4G5N5.js.map +1 -0
  40. package/dist/{chunk-GAG7QOVO.js → chunk-64WHKBMP.js} +53 -18
  41. package/dist/chunk-64WHKBMP.js.map +1 -0
  42. package/dist/{chunk-7TSYBD3E.js → chunk-65JXA63J.js} +2 -2
  43. package/dist/{chunk-Z45YRGFO.js → chunk-6NR36UNB.js} +2 -2
  44. package/dist/{chunk-W77KZHCW.js → chunk-6RH7PRTL.js} +16 -17
  45. package/dist/chunk-6RH7PRTL.js.map +1 -0
  46. package/dist/{chunk-3JVXVJ7L.js → chunk-7OBIBPCV.js} +10 -10
  47. package/dist/{chunk-AR2JDFAE.js → chunk-7Q3RHOA5.js} +2 -2
  48. package/dist/chunk-7ZGPQOFV.js +190 -0
  49. package/dist/chunk-7ZGPQOFV.js.map +1 -0
  50. package/dist/{chunk-QULVCV3M.js → chunk-AJGGBXWA.js} +4 -4
  51. package/dist/{chunk-CADUYBQD.js → chunk-ATL3ZVXT.js} +2 -2
  52. package/dist/{chunk-DMQCLZ4Z.js → chunk-ATY3ZASK.js} +2 -2
  53. package/dist/{chunk-4HDYQL3J.js → chunk-B4CRUZPR.js} +2 -2
  54. package/dist/{chunk-RV3ARMPN.js → chunk-BMU2DS3S.js} +30 -21
  55. package/dist/chunk-BMU2DS3S.js.map +1 -0
  56. package/dist/{chunk-6SAFL5YE.js → chunk-BZ3WMOYX.js} +3 -3
  57. package/dist/{chunk-UHXCLPGC.js → chunk-C3XWJCG7.js} +2 -2
  58. package/dist/chunk-C3XWJCG7.js.map +1 -0
  59. package/dist/{chunk-HXGUIPQB.js → chunk-CKKN25P2.js} +3 -3
  60. package/dist/{chunk-3URQC7EH.js → chunk-CXPOG3ZY.js} +355 -132
  61. package/dist/chunk-CXPOG3ZY.js.map +1 -0
  62. package/dist/{chunk-3FSX6LDG.js → chunk-D2YFQ7XG.js} +18 -3
  63. package/dist/{chunk-3FSX6LDG.js.map → chunk-D2YFQ7XG.js.map} +1 -1
  64. package/dist/{chunk-52JPHN5N.js → chunk-D6SXRMZZ.js} +27 -149
  65. package/dist/chunk-D6SXRMZZ.js.map +1 -0
  66. package/dist/{chunk-2DHSSLOD.js → chunk-DECNV4QK.js} +13 -15
  67. package/dist/chunk-DECNV4QK.js.map +1 -0
  68. package/dist/{chunk-6BU3L4UE.js → chunk-DNKVKO7K.js} +7 -10
  69. package/dist/chunk-DNKVKO7K.js.map +1 -0
  70. package/dist/{chunk-6HFM7EIT.js → chunk-EHRIDNZX.js} +2 -2
  71. package/dist/{chunk-3737A6EA.js → chunk-EHW7NLGD.js} +127 -31
  72. package/dist/chunk-EHW7NLGD.js.map +1 -0
  73. package/dist/{chunk-BEFVYAR5.js → chunk-EQOTJL76.js} +4 -4
  74. package/dist/chunk-EQOTJL76.js.map +1 -0
  75. package/dist/{chunk-E6427JU2.js → chunk-EV2TDHQZ.js} +19 -3
  76. package/dist/chunk-EV2TDHQZ.js.map +1 -0
  77. package/dist/{chunk-WFUIE565.js → chunk-FNYCT27B.js} +17 -14
  78. package/dist/chunk-FNYCT27B.js.map +1 -0
  79. package/dist/{chunk-RTAMLDWP.js → chunk-FU5VYEYK.js} +2 -2
  80. package/dist/chunk-FX3UIDCS.js +66 -0
  81. package/dist/chunk-FX3UIDCS.js.map +1 -0
  82. package/dist/{chunk-7F4DBNSP.js → chunk-GANFIM2M.js} +135 -18
  83. package/dist/chunk-GANFIM2M.js.map +1 -0
  84. package/dist/{chunk-4VUPLMZ6.js → chunk-GCU42IG7.js} +66 -71
  85. package/dist/chunk-GCU42IG7.js.map +1 -0
  86. package/dist/{chunk-RXYYFSBB.js → chunk-GKTW3KLV.js} +4 -4
  87. package/dist/chunk-HK67PX4J.js +27 -0
  88. package/dist/chunk-HK67PX4J.js.map +1 -0
  89. package/dist/{chunk-OF3L2SD6.js → chunk-HOPNIR5R.js} +2 -2
  90. package/dist/{chunk-WLZ47GSL.js → chunk-HSJBJIB2.js} +12 -10
  91. package/dist/{chunk-WLZ47GSL.js.map → chunk-HSJBJIB2.js.map} +1 -1
  92. package/dist/{chunk-ADW22QSB.js → chunk-HWI7GHJZ.js} +4 -4
  93. package/dist/{chunk-GQ3BX5EA.js → chunk-I3ISCYT7.js} +2 -2
  94. package/dist/{chunk-YJ6IONGA.js → chunk-IWKCJCLP.js} +11 -3
  95. package/dist/{chunk-YJ6IONGA.js.map → chunk-IWKCJCLP.js.map} +1 -1
  96. package/dist/{chunk-NJDQ4YSV.js → chunk-IWQPYT6S.js} +4 -4
  97. package/dist/{chunk-JRKZTEZZ.js → chunk-J6I5VR6P.js} +444 -73
  98. package/dist/chunk-J6I5VR6P.js.map +1 -0
  99. package/dist/{chunk-2Z3NCLNX.js → chunk-JHYKSMVX.js} +7 -7
  100. package/dist/{chunk-6FJWSWFT.js → chunk-JIHWBKMQ.js} +3 -3
  101. package/dist/{chunk-YAWK6AWF.js → chunk-JKATP6OE.js} +2 -2
  102. package/dist/{chunk-PL2GHSCM.js → chunk-JMJSCI7U.js} +157 -90
  103. package/dist/chunk-JMJSCI7U.js.map +1 -0
  104. package/dist/{chunk-Q6TEJXBX.js → chunk-JXHNUYQO.js} +31 -49
  105. package/dist/chunk-JXHNUYQO.js.map +1 -0
  106. package/dist/{chunk-2GAJGUNM.js → chunk-JYPRZWCZ.js} +343 -229
  107. package/dist/chunk-JYPRZWCZ.js.map +1 -0
  108. package/dist/{chunk-5ZKS6ZE2.js → chunk-K6CPIK3R.js} +4 -4
  109. package/dist/chunk-K6CPIK3R.js.map +1 -0
  110. package/dist/{chunk-CDB2HVQW.js → chunk-KM6ZXG7U.js} +14 -15
  111. package/dist/chunk-KM6ZXG7U.js.map +1 -0
  112. package/dist/{chunk-DG27IUVI.js → chunk-LC6NE4IQ.js} +2 -2
  113. package/dist/{chunk-WASHZV34.js → chunk-LIR2DJWP.js} +63 -25
  114. package/dist/chunk-LIR2DJWP.js.map +1 -0
  115. package/dist/{chunk-VZ3XFUQ7.js → chunk-LUYPYCHY.js} +16 -22
  116. package/dist/chunk-LUYPYCHY.js.map +1 -0
  117. package/dist/{chunk-G6S5VVHP.js → chunk-M7WBE3OR.js} +10 -10
  118. package/dist/chunk-M7WBE3OR.js.map +1 -0
  119. package/dist/{chunk-VJFMXYKP.js → chunk-MPKP6L5E.js} +2 -2
  120. package/dist/{chunk-IGJCYUT5.js → chunk-NSITFOCJ.js} +1 -1
  121. package/dist/chunk-NSITFOCJ.js.map +1 -0
  122. package/dist/{chunk-FUHKW252.js → chunk-OCJ3JA4O.js} +2 -2
  123. package/dist/{chunk-VCETL77O.js → chunk-ONPVHX2Y.js} +36 -7
  124. package/dist/chunk-ONPVHX2Y.js.map +1 -0
  125. package/dist/{chunk-WLYIROCJ.js → chunk-OQMCFHUX.js} +2 -2
  126. package/dist/{chunk-JRCYZEHU.js → chunk-OR2FYRYR.js} +2 -2
  127. package/dist/{chunk-HNHCQ556.js → chunk-PFWE7FJG.js} +2 -2
  128. package/dist/{chunk-NPP5R7FF.js → chunk-PJX72ICF.js} +22 -18
  129. package/dist/chunk-PJX72ICF.js.map +1 -0
  130. package/dist/{chunk-U5O6GIWZ.js → chunk-POFYIGQ2.js} +3 -3
  131. package/dist/{chunk-35EBKWQU.js → chunk-QH47SQLD.js} +4 -4
  132. package/dist/{chunk-FZ6NYJ7L.js → chunk-QQRRKO7W.js} +11 -8
  133. package/dist/chunk-QQRRKO7W.js.map +1 -0
  134. package/dist/{chunk-HVQGCKY4.js → chunk-QVXZFJBU.js} +2 -2
  135. package/dist/{chunk-FMQJZH4R.js → chunk-RDB66EKD.js} +6 -6
  136. package/dist/{chunk-X56RTUK3.js → chunk-RJZQ2PVD.js} +3 -3
  137. package/dist/{chunk-SUFG5E73.js → chunk-RPRASGEY.js} +2 -2
  138. package/dist/{chunk-XWX7GL56.js → chunk-S2I5MYSA.js} +7 -10
  139. package/dist/chunk-S2I5MYSA.js.map +1 -0
  140. package/dist/{chunk-FFDNKRCY.js → chunk-S6PJ6ZKE.js} +2 -2
  141. package/dist/chunk-S6PJ6ZKE.js.map +1 -0
  142. package/dist/{chunk-JIUVJ4M7.js → chunk-S7PM7Z4K.js} +3 -3
  143. package/dist/{chunk-55LKVALY.js → chunk-T7IR4N2S.js} +1 -3
  144. package/dist/chunk-T7IR4N2S.js.map +1 -0
  145. package/dist/{chunk-N35NC4JH.js → chunk-TKBB62DN.js} +2 -2
  146. package/dist/{chunk-YPYHVOXP.js → chunk-UJ4Z67UQ.js} +2 -2
  147. package/dist/chunk-UJ4Z67UQ.js.map +1 -0
  148. package/dist/{chunk-7MNBICB5.js → chunk-W3HAIN6U.js} +16 -14
  149. package/dist/chunk-W3HAIN6U.js.map +1 -0
  150. package/dist/{chunk-4UVDI4XE.js → chunk-WPZ5FVAS.js} +4 -4
  151. package/dist/{chunk-MSWYQLMQ.js → chunk-XINT4VJA.js} +2 -2
  152. package/dist/{chunk-7G6VDWXB.js → chunk-XPEPMK4Y.js} +3 -3
  153. package/dist/chunk-XPEPMK4Y.js.map +1 -0
  154. package/dist/{chunk-W3TVWF6N.js → chunk-YCGLAANM.js} +2 -2
  155. package/dist/{chunk-O2ERS4UJ.js → chunk-YTUFV7U7.js} +80 -51
  156. package/dist/chunk-YTUFV7U7.js.map +1 -0
  157. package/dist/{chunk-FDX7OWWI.js → chunk-YYV53EHO.js} +2 -2
  158. package/dist/{chunk-HNGSV2YC.js → chunk-Z3BZXPRO.js} +67 -10
  159. package/dist/chunk-Z3BZXPRO.js.map +1 -0
  160. package/dist/{chunk-6NVPFKN3.js → chunk-ZIMQDXTQ.js} +92 -35
  161. package/dist/chunk-ZIMQDXTQ.js.map +1 -0
  162. package/dist/{chunk-5JY5TSVY.js → chunk-ZJUZOJVW.js} +25 -31
  163. package/dist/chunk-ZJUZOJVW.js.map +1 -0
  164. package/dist/{chunk-3IXIU6FP.js → chunk-ZY6P7M52.js} +7 -10
  165. package/dist/chunk-ZY6P7M52.js.map +1 -0
  166. package/dist/{chunk-BWVKEOPE.js → chunk-ZYZJLYNS.js} +7 -11
  167. package/dist/chunk-ZYZJLYNS.js.map +1 -0
  168. package/dist/classified/index.js +5 -5
  169. package/dist/{classified-marker-2KJAXRRO.js → classified-marker-AZDSDG6V.js} +3 -3
  170. package/dist/collection-facade-WSUAYFJ4.js +45 -0
  171. package/dist/{computed-6G5M3UPH.js → computed-VG2PCM4L.js} +3 -3
  172. package/dist/consent/index.js +10 -10
  173. package/dist/cover/index.js +10 -3
  174. package/dist/custody/index.js +12 -11
  175. package/dist/{dead-filter-DCB62VWW.js → dead-filter-JNQNB3TA.js} +2 -2
  176. package/dist/debug/index.js +1 -1
  177. package/dist/{delegation-MO7YZYJS.js → delegation-5F36DRDH.js} +24 -23
  178. package/dist/delegation-5F36DRDH.js.map +1 -0
  179. package/dist/derivations/index.js +12 -12
  180. package/dist/derive-MPOGZ4NR.js +22 -0
  181. package/dist/directory/index.js +10 -10
  182. package/dist/{dispatch-MJ6SHB3Y.js → dispatch-3ZA733EY.js} +18 -18
  183. package/dist/{dispatch-FZEAWKVM.js → dispatch-TNM5CRLZ.js} +9 -6
  184. package/dist/dispatch-TNM5CRLZ.js.map +1 -0
  185. package/dist/{enclave-JU4YIEPB.js → enclave-Y2ALNDRD.js} +31 -9
  186. package/dist/executor-BBEEUFO3.js +9 -0
  187. package/dist/executor-G6G3YEWR.js +9 -0
  188. package/dist/executor-ZUTUMTPA.js +29 -0
  189. package/dist/export-accessible-S3KEB763.js +23 -0
  190. package/dist/extract-partition-KVCTSUVE.js +38 -0
  191. package/dist/{fanout-sidecar-NNAJQXMX.js → fanout-sidecar-3QZU7ZZX.js} +18 -27
  192. package/dist/fanout-sidecar-3QZU7ZZX.js.map +1 -0
  193. package/dist/find-V4FN5ITT.js +11 -0
  194. package/dist/forget/index.js +9 -9
  195. package/dist/guards/index.js +7 -7
  196. package/dist/history/index.js +23 -20
  197. package/dist/history/index.js.map +1 -1
  198. package/dist/i18n/index.js +14 -14
  199. package/dist/index.d.ts +30 -13
  200. package/dist/index.js +180 -188
  201. package/dist/index.js.map +1 -1
  202. package/dist/indexing/index.js +4 -4
  203. package/dist/introspection/index.js +11 -11
  204. package/dist/issue-5CFBWBRX.js +19 -0
  205. package/dist/kernel/collection.d.ts +8 -2
  206. package/dist/kernel/constants.d.ts +36 -0
  207. package/dist/kernel/enclave/crypto.d.ts +52 -4
  208. package/dist/kernel/enclave/index.d.ts +10 -3
  209. package/dist/kernel/enclave/record-aad.d.ts +131 -0
  210. package/dist/kernel/enclave/record-envelope.d.ts +76 -0
  211. package/dist/kernel/enclave/record-keys/envelope-body.d.ts +34 -2
  212. package/dist/kernel/enclave/record-keys/lifecycle.d.ts +22 -5
  213. package/dist/kernel/enclave/record-keys/record-codec.d.ts +47 -14
  214. package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +88 -0
  215. package/dist/kernel/enclave/record-keys/rekey.d.ts +91 -0
  216. package/dist/kernel/enclave/record-keys/tombstone.d.ts +3 -2
  217. package/dist/kernel/errors.d.ts +132 -12
  218. package/dist/kernel/merge-authority.d.ts +25 -0
  219. package/dist/kernel/noydb.d.ts +27 -4
  220. package/dist/kernel/query/builder.d.ts +51 -4
  221. package/dist/kernel/query/join.d.ts +27 -0
  222. package/dist/kernel/query/live.d.ts +31 -9
  223. package/dist/kernel/query/predicate.d.ts +9 -0
  224. package/dist/kernel/refs.d.ts +3 -1
  225. package/dist/kernel/sync-policy.d.ts +0 -2
  226. package/dist/kernel/tier-visibility.d.ts +34 -0
  227. package/dist/kernel/types.d.ts +111 -13
  228. package/dist/kernel/vault.d.ts +6 -6
  229. package/dist/kernel/via/index.d.ts +11 -2
  230. package/dist/kernel/via/reconcile.d.ts +12 -0
  231. package/dist/{ledger-AETNF4OM.js → ledger-MQIIOTZB.js} +11 -11
  232. package/dist/liberate-LW7OKVBH.js +25 -0
  233. package/dist/{link-set-E5HNY67I.js → link-set-5PJBBR63.js} +11 -11
  234. package/dist/{managed-secret-CWQ6PND5.js → managed-secret-ETDG6AAE.js} +11 -3
  235. package/dist/materialized-views/index.js +24 -24
  236. package/dist/migrate-cek-V3VDRV24.js +19 -0
  237. package/dist/migrate-cek-V3VDRV24.js.map +1 -0
  238. package/dist/money/index.js +5 -5
  239. package/dist/noydb-LFDRMYZ5.js +85 -0
  240. package/dist/overlay-views/index.js +4 -4
  241. package/dist/periods/index.js +12 -12
  242. package/dist/pod/index.js +79 -89
  243. package/dist/pod-handle-AZ3CXY5Y.js +41 -0
  244. package/dist/pod-handle-AZ3CXY5Y.js.map +1 -0
  245. package/dist/policy/index.js +11 -4
  246. package/dist/port/to/index.d.ts +4 -4
  247. package/dist/port/to/locator.d.ts +54 -8
  248. package/dist/port/with/i18n-strategy.d.ts +4 -1
  249. package/dist/port/with/lookup-strategy.d.ts +4 -1
  250. package/dist/port/with/merge-authority.d.ts +52 -0
  251. package/dist/port/with/service-bus.d.ts +0 -4
  252. package/dist/port/with/strategies.d.ts +2 -0
  253. package/dist/port/with/team-strategy.d.ts +5 -1
  254. package/dist/portability/index.js +8 -8
  255. package/dist/{post-register-2XSSMGEQ.js → post-register-QCJYRMIK.js} +6 -6
  256. package/dist/query/index.js +6 -6
  257. package/dist/reduce/index.js +6 -6
  258. package/dist/register-JBOEHTVX.js +32 -0
  259. package/dist/registry-5DYHQFJS.js +9 -0
  260. package/dist/registry-EANV44W2.js +9 -0
  261. package/dist/registry-J25H6VB4.js +19 -0
  262. package/dist/registry-R23SQUQH.js +18 -0
  263. package/dist/request-withdrawal-KQ4W4UT5.js +31 -0
  264. package/dist/{reveal-BW36KCOQ.js → reveal-W7BKZSUP.js} +6 -6
  265. package/dist/revoke-H3VSTAIE.js +24 -0
  266. package/dist/satellites/index.js +1 -1
  267. package/dist/schema-update/index.js +2 -2
  268. package/dist/sealed-record/index.js +20 -13
  269. package/dist/sealed-record/index.js.map +1 -1
  270. package/dist/search/index.js +4 -4
  271. package/dist/{seed-GDV5PT4R.js → seed-55FPOMYP.js} +20 -22
  272. package/dist/seed-55FPOMYP.js.map +1 -0
  273. package/dist/sequence/index.js +9 -9
  274. package/dist/session/index.js +10 -10
  275. package/dist/shadow/index.js +2 -2
  276. package/dist/share-link/index.js +1 -1
  277. package/dist/signer-53AQ3BU2.js +25 -0
  278. package/dist/snapshots/index.js +10 -10
  279. package/dist/snapshots/index.js.map +1 -1
  280. package/dist/{stale-ARHFVL5U.js → stale-TH7SSVRV.js} +10 -10
  281. package/dist/storage-WSF7ZKTL.js +23 -0
  282. package/dist/{store-coordination-provider-GFCUOBR6.js → store-coordination-provider-R27VRD6K.js} +18 -12
  283. package/dist/store-coordination-provider-R27VRD6K.js.map +1 -0
  284. package/dist/sync/index.js +11 -11
  285. package/dist/team/index.js +15 -14
  286. package/dist/tiers/index.js +11 -10
  287. package/dist/to/index.js +17 -9
  288. package/dist/to/index.js.map +1 -1
  289. package/dist/transactions/index.js +4 -4
  290. package/dist/{ulid-PTAIMDG4.js → ulid-VQW6ERQN.js} +2 -2
  291. package/dist/util/index.js +1 -1
  292. package/dist/vault-head/index.js +115 -0
  293. package/dist/vault-head/index.js.map +1 -0
  294. package/dist/{verify-RO5RTZOJ.js → verify-M5LQH43C.js} +5 -5
  295. package/dist/via/blob/index.d.ts +3 -0
  296. package/dist/via/lookup/handle.d.ts +4 -1
  297. package/dist/walk-NABCHDZN.js +21 -0
  298. package/dist/with-audit/periods/periods.d.ts +1 -1
  299. package/dist/with-audit/periods/strategy.d.ts +1 -1
  300. package/dist/with-audit/sealed-record/index.d.ts +13 -0
  301. package/dist/with-audit/tiers/index.d.ts +17 -2
  302. package/dist/with-cargo/adopt-partition.d.ts +1 -1
  303. package/dist/with-cargo/extract-partition.d.ts +1 -1
  304. package/dist/with-cargo/index.d.ts +1 -1
  305. package/dist/with-commit/history/history.d.ts +26 -0
  306. package/dist/with-commit/history/index.d.ts +2 -1
  307. package/dist/with-commit/history/ledger/store.d.ts +7 -1
  308. package/dist/with-commit/history/strategy.d.ts +11 -1
  309. package/dist/with-commit/numbering/index.d.ts +0 -5
  310. package/dist/with-commit/vault-head/active.d.ts +2 -0
  311. package/dist/with-commit/vault-head/head.d.ts +28 -0
  312. package/dist/with-commit/vault-head/index.d.ts +10 -0
  313. package/dist/with-commit/vault-head/strategy.d.ts +127 -0
  314. package/dist/with-commit/vault-head/verify.d.ts +90 -0
  315. package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +4 -0
  316. package/dist/with-formula/materialized-views/projection-legs.d.ts +59 -0
  317. package/dist/with-formula/materialized-views/registry.d.ts +50 -0
  318. package/dist/with-formula/materialized-views/types.d.ts +68 -10
  319. package/dist/with-party/directory/cover/storage.d.ts +0 -8
  320. package/dist/with-party/directory/storage.d.ts +0 -11
  321. package/dist/with-party/directory/visibility.d.ts +0 -21
  322. package/dist/with-party/policy/storage.d.ts +0 -10
  323. package/dist/with-party/team/index.d.ts +1 -1
  324. package/dist/with-party/team/keyring.d.ts +139 -2
  325. package/dist/with-party/team/noydb-facade.d.ts +5 -1
  326. package/dist/with-party/team/roster-tag.d.ts +101 -0
  327. package/dist/with-party/team/tiers.d.ts +2 -1
  328. package/dist/with-pod/format.d.ts +10 -12
  329. package/dist/with-pod/index.d.ts +10 -10
  330. package/dist/with-pod/open.d.ts +2 -2
  331. package/dist/with-pod/pod-handle.d.ts +19 -0
  332. package/dist/with-pod/pod-store.d.ts +0 -8
  333. package/dist/with-pod/{bundle.d.ts → pod.d.ts} +11 -19
  334. package/dist/with-pod/ulid.d.ts +1 -1
  335. package/dist/with-shape/blobs/blob-compaction.d.ts +13 -0
  336. package/dist/with-shape/blobs/blob-set.d.ts +83 -11
  337. package/dist/with-shape/blobs/orphan-report.d.ts +62 -0
  338. package/dist/with-sync/engine.d.ts +54 -5
  339. package/dist/with-sync/strategy.d.ts +3 -0
  340. package/dist/withdraw-accessible-VCINIG6V.js +28 -0
  341. package/package.json +10 -4
  342. package/dist/api-RXUMEKT3.js +0 -20
  343. package/dist/backup-KULFZ2T4.js.map +0 -1
  344. package/dist/bundle-handle-GFNHT52M.js +0 -36
  345. package/dist/bundle-handle-GFNHT52M.js.map +0 -1
  346. package/dist/chunk-2DHSSLOD.js.map +0 -1
  347. package/dist/chunk-2GAJGUNM.js.map +0 -1
  348. package/dist/chunk-3737A6EA.js.map +0 -1
  349. package/dist/chunk-3IXIU6FP.js.map +0 -1
  350. package/dist/chunk-3URQC7EH.js.map +0 -1
  351. package/dist/chunk-4GZ6XOVW.js.map +0 -1
  352. package/dist/chunk-4I72F5DF.js.map +0 -1
  353. package/dist/chunk-4VUPLMZ6.js.map +0 -1
  354. package/dist/chunk-52JPHN5N.js.map +0 -1
  355. package/dist/chunk-55LKVALY.js.map +0 -1
  356. package/dist/chunk-5JY5TSVY.js.map +0 -1
  357. package/dist/chunk-5ZKS6ZE2.js.map +0 -1
  358. package/dist/chunk-6BU3L4UE.js.map +0 -1
  359. package/dist/chunk-6NVPFKN3.js.map +0 -1
  360. package/dist/chunk-7F4DBNSP.js.map +0 -1
  361. package/dist/chunk-7G6VDWXB.js.map +0 -1
  362. package/dist/chunk-7H73RIE3.js +0 -45
  363. package/dist/chunk-7H73RIE3.js.map +0 -1
  364. package/dist/chunk-7MNBICB5.js.map +0 -1
  365. package/dist/chunk-AFHQATY4.js +0 -47
  366. package/dist/chunk-AFHQATY4.js.map +0 -1
  367. package/dist/chunk-AHLJSVRL.js.map +0 -1
  368. package/dist/chunk-BEFVYAR5.js.map +0 -1
  369. package/dist/chunk-BRVW2GS5.js.map +0 -1
  370. package/dist/chunk-BWVKEOPE.js.map +0 -1
  371. package/dist/chunk-CDB2HVQW.js.map +0 -1
  372. package/dist/chunk-CEJWMBXO.js.map +0 -1
  373. package/dist/chunk-E6427JU2.js.map +0 -1
  374. package/dist/chunk-FFDNKRCY.js.map +0 -1
  375. package/dist/chunk-FZ6NYJ7L.js.map +0 -1
  376. package/dist/chunk-G6S5VVHP.js.map +0 -1
  377. package/dist/chunk-GAG7QOVO.js.map +0 -1
  378. package/dist/chunk-HNGSV2YC.js.map +0 -1
  379. package/dist/chunk-IGJCYUT5.js.map +0 -1
  380. package/dist/chunk-JJNOLSG5.js.map +0 -1
  381. package/dist/chunk-JRKZTEZZ.js.map +0 -1
  382. package/dist/chunk-NPP5R7FF.js.map +0 -1
  383. package/dist/chunk-O2ERS4UJ.js.map +0 -1
  384. package/dist/chunk-PDLLCOGD.js.map +0 -1
  385. package/dist/chunk-PL2GHSCM.js.map +0 -1
  386. package/dist/chunk-PNKHAKQ7.js.map +0 -1
  387. package/dist/chunk-Q6TEJXBX.js.map +0 -1
  388. package/dist/chunk-RV3ARMPN.js.map +0 -1
  389. package/dist/chunk-UHXCLPGC.js.map +0 -1
  390. package/dist/chunk-VCETL77O.js.map +0 -1
  391. package/dist/chunk-VZ3XFUQ7.js.map +0 -1
  392. package/dist/chunk-W77KZHCW.js.map +0 -1
  393. package/dist/chunk-WASHZV34.js.map +0 -1
  394. package/dist/chunk-WFUIE565.js.map +0 -1
  395. package/dist/chunk-XWX7GL56.js.map +0 -1
  396. package/dist/chunk-YPYHVOXP.js.map +0 -1
  397. package/dist/chunk-ZJADGNYF.js.map +0 -1
  398. package/dist/collection-facade-QEJIM6BC.js +0 -45
  399. package/dist/delegation-MO7YZYJS.js.map +0 -1
  400. package/dist/derive-IUA6JFLB.js +0 -22
  401. package/dist/dispatch-FZEAWKVM.js.map +0 -1
  402. package/dist/executor-CYJQLKKI.js +0 -9
  403. package/dist/executor-S4QIKEHW.js +0 -9
  404. package/dist/executor-YIQB2S6N.js +0 -29
  405. package/dist/export-accessible-4S36PQNR.js +0 -23
  406. package/dist/extract-partition-IO7FDZ2P.js +0 -38
  407. package/dist/fanout-sidecar-NNAJQXMX.js.map +0 -1
  408. package/dist/find-RBBYKXI7.js +0 -11
  409. package/dist/issue-RIVHSY2S.js +0 -19
  410. package/dist/liberate-GFN4C7RR.js +0 -24
  411. package/dist/noydb-ASEXNJS6.js +0 -83
  412. package/dist/register-4EFJCVYD.js +0 -32
  413. package/dist/registry-4SDHHOJ7.js +0 -18
  414. package/dist/registry-CZVINVQH.js +0 -9
  415. package/dist/registry-IKUR2MXD.js +0 -9
  416. package/dist/registry-OYTFGWVK.js +0 -19
  417. package/dist/request-withdrawal-KGXGDPVQ.js +0 -31
  418. package/dist/revoke-JFN773KF.js +0 -24
  419. package/dist/seed-GDV5PT4R.js.map +0 -1
  420. package/dist/signer-TA55QB6R.js +0 -25
  421. package/dist/storage-V7MTFAQJ.js +0 -23
  422. package/dist/store-coordination-provider-GFCUOBR6.js.map +0 -1
  423. package/dist/walk-3ZECBFDA.js +0 -21
  424. package/dist/with-pod/bundle-handle.d.ts +0 -9
  425. package/dist/withdraw-accessible-7PG4DYQK.js +0 -28
  426. /package/dist/{api-RXUMEKT3.js.map → api-6X2VLZ7J.js.map} +0 -0
  427. /package/dist/{chunk-KUDZVJKQ.js.map → chunk-22GR3O4J.js.map} +0 -0
  428. /package/dist/{chunk-CCZJJ4I2.js.map → chunk-2CBFYXJX.js.map} +0 -0
  429. /package/dist/{chunk-FAXZQ5UF.js.map → chunk-323MN67Z.js.map} +0 -0
  430. /package/dist/{chunk-VMCCGUQB.js.map → chunk-36CEG6CM.js.map} +0 -0
  431. /package/dist/{chunk-747LRSOO.js.map → chunk-4K6BIMLF.js.map} +0 -0
  432. /package/dist/{chunk-FNSMZQS4.js.map → chunk-5IHGJHWC.js.map} +0 -0
  433. /package/dist/{chunk-HPLWFNG4.js.map → chunk-5O5ZSW7N.js.map} +0 -0
  434. /package/dist/{chunk-7TSYBD3E.js.map → chunk-65JXA63J.js.map} +0 -0
  435. /package/dist/{chunk-Z45YRGFO.js.map → chunk-6NR36UNB.js.map} +0 -0
  436. /package/dist/{chunk-3JVXVJ7L.js.map → chunk-7OBIBPCV.js.map} +0 -0
  437. /package/dist/{chunk-AR2JDFAE.js.map → chunk-7Q3RHOA5.js.map} +0 -0
  438. /package/dist/{chunk-QULVCV3M.js.map → chunk-AJGGBXWA.js.map} +0 -0
  439. /package/dist/{chunk-CADUYBQD.js.map → chunk-ATL3ZVXT.js.map} +0 -0
  440. /package/dist/{chunk-DMQCLZ4Z.js.map → chunk-ATY3ZASK.js.map} +0 -0
  441. /package/dist/{chunk-4HDYQL3J.js.map → chunk-B4CRUZPR.js.map} +0 -0
  442. /package/dist/{chunk-6SAFL5YE.js.map → chunk-BZ3WMOYX.js.map} +0 -0
  443. /package/dist/{chunk-HXGUIPQB.js.map → chunk-CKKN25P2.js.map} +0 -0
  444. /package/dist/{chunk-6HFM7EIT.js.map → chunk-EHRIDNZX.js.map} +0 -0
  445. /package/dist/{chunk-RTAMLDWP.js.map → chunk-FU5VYEYK.js.map} +0 -0
  446. /package/dist/{chunk-RXYYFSBB.js.map → chunk-GKTW3KLV.js.map} +0 -0
  447. /package/dist/{chunk-OF3L2SD6.js.map → chunk-HOPNIR5R.js.map} +0 -0
  448. /package/dist/{chunk-ADW22QSB.js.map → chunk-HWI7GHJZ.js.map} +0 -0
  449. /package/dist/{chunk-GQ3BX5EA.js.map → chunk-I3ISCYT7.js.map} +0 -0
  450. /package/dist/{chunk-NJDQ4YSV.js.map → chunk-IWQPYT6S.js.map} +0 -0
  451. /package/dist/{chunk-2Z3NCLNX.js.map → chunk-JHYKSMVX.js.map} +0 -0
  452. /package/dist/{chunk-6FJWSWFT.js.map → chunk-JIHWBKMQ.js.map} +0 -0
  453. /package/dist/{chunk-YAWK6AWF.js.map → chunk-JKATP6OE.js.map} +0 -0
  454. /package/dist/{chunk-DG27IUVI.js.map → chunk-LC6NE4IQ.js.map} +0 -0
  455. /package/dist/{chunk-VJFMXYKP.js.map → chunk-MPKP6L5E.js.map} +0 -0
  456. /package/dist/{chunk-FUHKW252.js.map → chunk-OCJ3JA4O.js.map} +0 -0
  457. /package/dist/{chunk-WLYIROCJ.js.map → chunk-OQMCFHUX.js.map} +0 -0
  458. /package/dist/{chunk-JRCYZEHU.js.map → chunk-OR2FYRYR.js.map} +0 -0
  459. /package/dist/{chunk-HNHCQ556.js.map → chunk-PFWE7FJG.js.map} +0 -0
  460. /package/dist/{chunk-U5O6GIWZ.js.map → chunk-POFYIGQ2.js.map} +0 -0
  461. /package/dist/{chunk-35EBKWQU.js.map → chunk-QH47SQLD.js.map} +0 -0
  462. /package/dist/{chunk-HVQGCKY4.js.map → chunk-QVXZFJBU.js.map} +0 -0
  463. /package/dist/{chunk-FMQJZH4R.js.map → chunk-RDB66EKD.js.map} +0 -0
  464. /package/dist/{chunk-X56RTUK3.js.map → chunk-RJZQ2PVD.js.map} +0 -0
  465. /package/dist/{chunk-SUFG5E73.js.map → chunk-RPRASGEY.js.map} +0 -0
  466. /package/dist/{chunk-JIUVJ4M7.js.map → chunk-S7PM7Z4K.js.map} +0 -0
  467. /package/dist/{chunk-N35NC4JH.js.map → chunk-TKBB62DN.js.map} +0 -0
  468. /package/dist/{chunk-4UVDI4XE.js.map → chunk-WPZ5FVAS.js.map} +0 -0
  469. /package/dist/{chunk-MSWYQLMQ.js.map → chunk-XINT4VJA.js.map} +0 -0
  470. /package/dist/{chunk-W3TVWF6N.js.map → chunk-YCGLAANM.js.map} +0 -0
  471. /package/dist/{chunk-FDX7OWWI.js.map → chunk-YYV53EHO.js.map} +0 -0
  472. /package/dist/{classified-marker-2KJAXRRO.js.map → classified-marker-AZDSDG6V.js.map} +0 -0
  473. /package/dist/{collection-facade-QEJIM6BC.js.map → collection-facade-WSUAYFJ4.js.map} +0 -0
  474. /package/dist/{computed-6G5M3UPH.js.map → computed-VG2PCM4L.js.map} +0 -0
  475. /package/dist/{dead-filter-DCB62VWW.js.map → dead-filter-JNQNB3TA.js.map} +0 -0
  476. /package/dist/{derive-IUA6JFLB.js.map → derive-MPOGZ4NR.js.map} +0 -0
  477. /package/dist/{dispatch-MJ6SHB3Y.js.map → dispatch-3ZA733EY.js.map} +0 -0
  478. /package/dist/{enclave-JU4YIEPB.js.map → enclave-Y2ALNDRD.js.map} +0 -0
  479. /package/dist/{executor-CYJQLKKI.js.map → executor-BBEEUFO3.js.map} +0 -0
  480. /package/dist/{executor-S4QIKEHW.js.map → executor-G6G3YEWR.js.map} +0 -0
  481. /package/dist/{executor-YIQB2S6N.js.map → executor-ZUTUMTPA.js.map} +0 -0
  482. /package/dist/{export-accessible-4S36PQNR.js.map → export-accessible-S3KEB763.js.map} +0 -0
  483. /package/dist/{extract-partition-IO7FDZ2P.js.map → extract-partition-KVCTSUVE.js.map} +0 -0
  484. /package/dist/{find-RBBYKXI7.js.map → find-V4FN5ITT.js.map} +0 -0
  485. /package/dist/{issue-RIVHSY2S.js.map → issue-5CFBWBRX.js.map} +0 -0
  486. /package/dist/{ledger-AETNF4OM.js.map → ledger-MQIIOTZB.js.map} +0 -0
  487. /package/dist/{liberate-GFN4C7RR.js.map → liberate-LW7OKVBH.js.map} +0 -0
  488. /package/dist/{link-set-E5HNY67I.js.map → link-set-5PJBBR63.js.map} +0 -0
  489. /package/dist/{managed-secret-CWQ6PND5.js.map → managed-secret-ETDG6AAE.js.map} +0 -0
  490. /package/dist/{noydb-ASEXNJS6.js.map → noydb-LFDRMYZ5.js.map} +0 -0
  491. /package/dist/{post-register-2XSSMGEQ.js.map → post-register-QCJYRMIK.js.map} +0 -0
  492. /package/dist/{register-4EFJCVYD.js.map → register-JBOEHTVX.js.map} +0 -0
  493. /package/dist/{registry-4SDHHOJ7.js.map → registry-5DYHQFJS.js.map} +0 -0
  494. /package/dist/{registry-CZVINVQH.js.map → registry-EANV44W2.js.map} +0 -0
  495. /package/dist/{registry-IKUR2MXD.js.map → registry-J25H6VB4.js.map} +0 -0
  496. /package/dist/{registry-OYTFGWVK.js.map → registry-R23SQUQH.js.map} +0 -0
  497. /package/dist/{request-withdrawal-KGXGDPVQ.js.map → request-withdrawal-KQ4W4UT5.js.map} +0 -0
  498. /package/dist/{reveal-BW36KCOQ.js.map → reveal-W7BKZSUP.js.map} +0 -0
  499. /package/dist/{revoke-JFN773KF.js.map → revoke-H3VSTAIE.js.map} +0 -0
  500. /package/dist/{signer-TA55QB6R.js.map → signer-53AQ3BU2.js.map} +0 -0
  501. /package/dist/{stale-ARHFVL5U.js.map → stale-TH7SSVRV.js.map} +0 -0
  502. /package/dist/{storage-V7MTFAQJ.js.map → storage-WSF7ZKTL.js.map} +0 -0
  503. /package/dist/{ulid-PTAIMDG4.js.map → ulid-VQW6ERQN.js.map} +0 -0
  504. /package/dist/{verify-RO5RTZOJ.js.map → verify-M5LQH43C.js.map} +0 -0
  505. /package/dist/{walk-3ZECBFDA.js.map → walk-NABCHDZN.js.map} +0 -0
  506. /package/dist/{withdraw-accessible-7PG4DYQK.js.map → withdraw-accessible-VCINIG6V.js.map} +0 -0
@@ -5,7 +5,18 @@ import {
5
5
  readDirectoryConfig,
6
6
  readUserVisibility,
7
7
  saveUserEnvelope
8
- } from "./chunk-5JY5TSVY.js";
8
+ } from "./chunk-ZJUZOJVW.js";
9
+ import {
10
+ parseDekKey
11
+ } from "./chunk-D2YFQ7XG.js";
12
+ import {
13
+ envelopeOpensUnderAny,
14
+ rekeyBlobSet,
15
+ rekeyEnvelopeIfNeeded
16
+ } from "./chunk-CXPOG3ZY.js";
17
+ import {
18
+ buildRecordEnvelope
19
+ } from "./chunk-FX3UIDCS.js";
9
20
  import {
10
21
  base64ToBuffer,
11
22
  bufferToBase64,
@@ -20,25 +31,104 @@ import {
20
31
  generateSalt,
21
32
  unwrapKey,
22
33
  wrapKey
23
- } from "./chunk-VCETL77O.js";
34
+ } from "./chunk-ONPVHX2Y.js";
24
35
  import {
25
- NOYDB_FORMAT_VERSION,
26
36
  NOYDB_KEYRING_VERSION
27
- } from "./chunk-YPYHVOXP.js";
37
+ } from "./chunk-UJ4Z67UQ.js";
28
38
  import {
39
+ BLOB_ADDRESS_KEY_ID,
29
40
  DirectoryDisabledError,
30
41
  EchoCeremonyRequiredError,
31
42
  InvalidKeyError,
32
43
  KeyringCorruptError,
33
44
  KeyringExpiredError,
45
+ KeyringTamperedError,
34
46
  NoAccessError,
35
47
  NoydbError,
36
48
  PermissionDeniedError,
37
49
  PrivilegeEscalationError,
50
+ ROSTER_KEY_ID,
38
51
  USER_ENVELOPE_COLLECTION,
39
52
  ValidationError,
40
53
  WrongPromptError
41
- } from "./chunk-HNGSV2YC.js";
54
+ } from "./chunk-Z3BZXPRO.js";
55
+
56
+ // src/with-party/team/roster-tag.ts
57
+ function stable(value) {
58
+ if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
59
+ if (value !== null && typeof value === "object") {
60
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${stable(v)}`);
61
+ return `{${entries.join(",")}}`;
62
+ }
63
+ return JSON.stringify(value) ?? "null";
64
+ }
65
+ function rosterCanonical(file) {
66
+ return stable({
67
+ user_id: file.user_id,
68
+ role: file.role,
69
+ permissions: file.permissions,
70
+ granted_by: file.granted_by,
71
+ // #1115 — WHICH collections this keyring holds a key for, authenticated.
72
+ //
73
+ // `revoke` derives its rotation scope from `Object.keys(target.deks)`, so
74
+ // while this set was unauthenticated a store could strip entries from the
75
+ // target's file and have those collections silently skipped by the
76
+ // rotation — leaving a revoked member, colluding with that store, holding
77
+ // live DEKs for exactly the collections it removed. That directly
78
+ // contradicts `SECURITY.md`'s "the rotation cannot be skipped".
79
+ //
80
+ // NAMES ONLY, deliberately. The wrapped values are AES-KW, which is
81
+ // self-authenticating: a tampered wrap fails to unwrap. What was
82
+ // unprotected is the SHAPE of the map, not its contents.
83
+ //
84
+ // `pending_deks` is bound for the same reason one field over: stripping it
85
+ // makes an interrupted rotation mint a fresh DEK instead of resuming,
86
+ // permanently orphaning every record already rewritten under the pending
87
+ // key (#1074). Leaving it out would recreate the identical hole.
88
+ dek_slots: Object.keys(file.deks ?? {}).sort(),
89
+ pending_dek_slots: Object.keys(file.pending_deks ?? {}).sort(),
90
+ expires_at: file.expires_at ?? null,
91
+ export_capability: file.export_capability ?? null,
92
+ import_capability: file.import_capability ?? null
93
+ });
94
+ }
95
+ async function mintRosterTag(file, rosterKey) {
96
+ return encrypt(rosterCanonical(file), rosterKey);
97
+ }
98
+ async function verifyRosterTag(file, tag, rosterKey) {
99
+ if (tag == null) return false;
100
+ try {
101
+ return await decrypt(tag.iv, tag.data, rosterKey) === rosterCanonical(file);
102
+ } catch {
103
+ return false;
104
+ }
105
+ }
106
+ async function assertRosterAuthenticated(file, deks, userId) {
107
+ const rosterKey = deks.get(ROSTER_KEY_ID);
108
+ if (rosterKey === void 0) {
109
+ throw new KeyringTamperedError({ userId, reason: "roster-key-missing" });
110
+ }
111
+ await assertRosterTagValid(file, rosterKey, userId);
112
+ }
113
+ async function assertRosterTagValid(file, rosterKey, userId) {
114
+ if (!await verifyRosterTag(file, file.roster_tag, rosterKey)) {
115
+ const reason = mismatchReason(file);
116
+ throw new KeyringTamperedError({
117
+ userId,
118
+ reason,
119
+ // Only on the format branch: elsewhere the declared version matches, so
120
+ // reporting a transition would invent one. `from` is what the FILE says
121
+ // — untrusted, and safe to surface for the same reason `mismatchReason`
122
+ // is safe to compute from it: it selects wording, never a decision.
123
+ ...reason === "format-superseded" ? { format: { from: Number(file._noydb_keyring), to: NOYDB_KEYRING_VERSION } } : {}
124
+ });
125
+ }
126
+ }
127
+ function mismatchReason(file) {
128
+ if (file.roster_tag == null) return "roster-tag-missing";
129
+ if (file._noydb_keyring !== NOYDB_KEYRING_VERSION) return "format-superseded";
130
+ return "roster-tag-mismatch";
131
+ }
42
132
 
43
133
  // src/kernel/validation.ts
44
134
  var WeakSecretError = class extends NoydbError {
@@ -294,6 +384,18 @@ async function verifyKeyringCanary(wrappedCanary, kek) {
294
384
  return false;
295
385
  }
296
386
  }
387
+ function rosterKeyOf(keyring) {
388
+ return keyring.deks.get(ROSTER_KEY_ID) ?? null;
389
+ }
390
+ function requireRosterKey(keyring, fn) {
391
+ const rosterKey = rosterKeyOf(keyring);
392
+ if (!rosterKey) {
393
+ throw new ValidationError(
394
+ `${fn}: caller keyring has no vault roster key \u2014 cannot stamp a roster tag. This typically means the keyring was opened via tier-3 PIN resume, session restore, or a wrap-DEKs tier-2 unlock. Re-authenticate at tier 1 (secret) before writing a keyring.`
395
+ );
396
+ }
397
+ return rosterKey;
398
+ }
297
399
  async function deriveKekForKeyring(file, secret, salt) {
298
400
  if (file.echo !== void 0) {
299
401
  if (typeof secret === "string") throw new EchoCeremonyRequiredError();
@@ -327,10 +429,12 @@ async function loadKeyring(store, vault, opts) {
327
429
  throw new NoAccessError(`No keyring found for user "${userId}" in vault "${vault}"`);
328
430
  }
329
431
  const { file: keyringFile } = found;
330
- assertKeyringNotExpired(keyringFile);
331
432
  const salt = base64ToBuffer(keyringFile.salt);
332
433
  const kek = await deriveKekForKeyring(keyringFile, secret, salt);
333
- const canaryOk = keyringFile.canary !== void 0 ? await verifyKeyringCanary(keyringFile.canary, kek) : null;
434
+ if (keyringFile.canary === void 0) {
435
+ throw new KeyringTamperedError({ userId, reason: "canary-missing" });
436
+ }
437
+ const canaryOk = await verifyKeyringCanary(keyringFile.canary, kek);
334
438
  const deks = /* @__PURE__ */ new Map();
335
439
  const failedCollections = [];
336
440
  let firstUnwrapError = null;
@@ -343,11 +447,18 @@ async function loadKeyring(store, vault, opts) {
343
447
  if (firstUnwrapError === null) firstUnwrapError = err;
344
448
  }
345
449
  }
346
- if (canaryOk === true) {
450
+ const pendingDeks = /* @__PURE__ */ new Map();
451
+ for (const [collName, wrapped] of Object.entries(keyringFile.pending_deks ?? {})) {
452
+ try {
453
+ pendingDeks.set(collName, await unwrapKey(wrapped, kek));
454
+ } catch {
455
+ }
456
+ }
457
+ if (canaryOk) {
347
458
  if (failedCollections.length > 0) {
348
459
  throw new KeyringCorruptError({ failedCollections, intactCount: deks.size });
349
460
  }
350
- } else if (canaryOk === false) {
461
+ } else {
351
462
  if (deks.size > 0) {
352
463
  throw new KeyringCorruptError({
353
464
  failedCollections: [...failedCollections, "_canary"],
@@ -355,20 +466,16 @@ async function loadKeyring(store, vault, opts) {
355
466
  });
356
467
  }
357
468
  throw firstUnwrapError instanceof Error ? firstUnwrapError : new InvalidKeyError();
358
- } else {
359
- if (failedCollections.length > 0) {
360
- if (deks.size > 0) {
361
- throw new KeyringCorruptError({ failedCollections, intactCount: deks.size });
362
- }
363
- throw firstUnwrapError instanceof Error ? firstUnwrapError : new InvalidKeyError();
364
- }
365
469
  }
470
+ await assertRosterAuthenticated(keyringFile, deks, userId);
471
+ assertKeyringNotExpired(keyringFile);
366
472
  return {
367
473
  userId: keyringFile.user_id,
368
474
  displayName: keyringFile.display_name,
369
475
  role: keyringFile.role,
370
476
  permissions: keyringFile.permissions,
371
477
  deks,
478
+ pendingDeks,
372
479
  kek,
373
480
  salt,
374
481
  authenticators: keyringFile.authenticators ?? [],
@@ -404,18 +511,33 @@ async function createOwnerKeyring(store, vault, opts) {
404
511
  const kek = typeof secret === "string" ? await deriveKey(secret, salt) : await deriveEchoKey(secret, salt);
405
512
  const userEnvelopeDek = await generateDEK();
406
513
  const wrappedUserEnvelopeDek = await wrapKey(userEnvelopeDek, kek);
514
+ const rosterKey = await generateDEK();
515
+ const wrappedRosterKey = await wrapKey(rosterKey, kek);
516
+ const blobAddressKey = await generateDEK();
517
+ const wrappedBlobAddressKey = await wrapKey(blobAddressKey, kek);
407
518
  const canary = await mintKeyringCanary(kek);
408
- const keyringFile = {
409
- _noydb_keyring: NOYDB_KEYRING_VERSION,
519
+ const authority = {
410
520
  user_id: userId,
411
521
  display_name: userId,
412
522
  role: "owner",
413
523
  permissions: {},
414
- deks: { [USER_ENVELOPE_COLLECTION]: wrappedUserEnvelopeDek },
524
+ granted_by: userId
525
+ };
526
+ const authorityWithDeks = {
527
+ ...authority,
528
+ deks: {
529
+ [USER_ENVELOPE_COLLECTION]: wrappedUserEnvelopeDek,
530
+ [ROSTER_KEY_ID]: wrappedRosterKey,
531
+ [BLOB_ADDRESS_KEY_ID]: wrappedBlobAddressKey
532
+ }
533
+ };
534
+ const keyringFile = {
535
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
536
+ ...authorityWithDeks,
415
537
  salt: bufferToBase64(salt),
416
538
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
417
- granted_by: userId,
418
539
  canary,
540
+ roster_tag: await mintRosterTag(authorityWithDeks, rosterKey),
419
541
  // The presence of this block is what makes the keyring an echo keyring —
420
542
  // `deriveKekForKeyring` reads it to refuse a single-string unlock (AG-1).
421
543
  ...typeof secret !== "string" ? {
@@ -432,7 +554,10 @@ async function createOwnerKeyring(store, vault, opts) {
432
554
  displayName: userId,
433
555
  role: "owner",
434
556
  permissions: {},
435
- deks: /* @__PURE__ */ new Map([[USER_ENVELOPE_COLLECTION, userEnvelopeDek]]),
557
+ deks: /* @__PURE__ */ new Map([
558
+ [USER_ENVELOPE_COLLECTION, userEnvelopeDek],
559
+ [ROSTER_KEY_ID, rosterKey]
560
+ ]),
436
561
  kek,
437
562
  salt,
438
563
  authenticators: []
@@ -444,6 +569,7 @@ async function grant(store, vault, callerKeyring, options) {
444
569
  "grant: caller keyring has no KEK \u2014 tier-2 wrap-DEKs and tier-3 PIN-resume sessions cannot grant access to other users. Re-authenticate at tier 1 (secret) before granting."
445
570
  );
446
571
  }
572
+ const callerRosterKey = requireRosterKey(callerKeyring, "grant");
447
573
  if (!canGrant(callerKeyring.role, options.role)) {
448
574
  throw new PermissionDeniedError(
449
575
  `Role "${callerKeyring.role}" cannot grant role "${options.role}"`
@@ -472,6 +598,8 @@ async function grant(store, vault, callerKeyring, options) {
472
598
  mintedForGrant = true;
473
599
  }
474
600
  if (mintedForGrant) await persistKeyring(store, vault, callerKeyring);
601
+ const previousGrantFound = await readKeyringFile(store, vault, options.userId);
602
+ const previousDekNames = previousGrantFound ? new Set(Object.keys(previousGrantFound.file.deks)) : null;
475
603
  const wrappedDeks = {};
476
604
  for (const collName of Object.keys(permissions)) {
477
605
  if (collName === PERMISSION_WILDCARD) continue;
@@ -499,21 +627,35 @@ async function grant(store, vault, callerKeyring, options) {
499
627
  }
500
628
  }
501
629
  const canary = await mintKeyringCanary(newKek);
502
- const keyringFile = {
503
- _noydb_keyring: NOYDB_KEYRING_VERSION,
630
+ const authority = {
504
631
  user_id: options.userId,
505
632
  display_name: options.displayName,
506
633
  role: options.role,
507
634
  permissions,
508
- deks: wrappedDeks,
509
- salt: bufferToBase64(newSalt),
510
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
511
635
  granted_by: callerKeyring.userId,
512
- canary,
513
636
  ...options.exportCapability !== void 0 && { export_capability: options.exportCapability },
514
637
  ...options.importCapability !== void 0 && { import_capability: options.importCapability }
515
638
  };
639
+ const authorityWithDeks = { ...authority, deks: wrappedDeks };
640
+ const keyringFile = {
641
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
642
+ ...authorityWithDeks,
643
+ salt: bufferToBase64(newSalt),
644
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
645
+ canary,
646
+ // The grantee's own copy of the roster key rode in via the `_`-prefix
647
+ // propagation loop above, so they can verify this tag on first unlock.
648
+ roster_tag: await mintRosterTag(authorityWithDeks, callerRosterKey)
649
+ };
516
650
  await writeKeyringFile(store, vault, options.userId, keyringFile);
651
+ if (previousDekNames !== null && options.userId !== callerKeyring.userId) {
652
+ const dropped = [...previousDekNames].filter(
653
+ (name) => !name.startsWith("_") && !(name in wrappedDeks)
654
+ );
655
+ if (dropped.length > 0) {
656
+ await rotateKeys(store, vault, callerKeyring, { collections: dropped });
657
+ }
658
+ }
517
659
  const userEnvelopeDek = callerKeyring.deks.get(USER_ENVELOPE_COLLECTION);
518
660
  if (userEnvelopeDek) {
519
661
  const initialPayload = options.initialProfile ?? {};
@@ -526,13 +668,14 @@ async function grant(store, vault, callerKeyring, options) {
526
668
  );
527
669
  }
528
670
  }
529
- async function findAdminDescendants(store, vault, rootUserId) {
671
+ async function findAdminDescendants(store, vault, rootUserId, rosterKey) {
530
672
  const allUserIds = await store.list(vault, "_keyring");
531
673
  const childrenByParent = /* @__PURE__ */ new Map();
532
674
  for (const userId of allUserIds) {
533
675
  const found = await readKeyringFile(store, vault, userId);
534
676
  if (!found) continue;
535
677
  const kf = found.file;
678
+ await assertRosterTagValid(kf, rosterKey, userId);
536
679
  if (kf.role !== "admin") continue;
537
680
  if (kf.user_id === rootUserId) continue;
538
681
  const list = childrenByParent.get(kf.granted_by) ?? [];
@@ -556,9 +699,16 @@ async function findAdminDescendants(store, vault, rootUserId) {
556
699
  async function revoke(store, vault, callerKeyring, options) {
557
700
  const targetFound = await readKeyringFile(store, vault, options.userId);
558
701
  if (!targetFound) {
702
+ const pending = [...callerKeyring.pendingDeks?.keys() ?? []];
703
+ if (pending.length > 0) {
704
+ await rotateKeys(store, vault, callerKeyring, { collections: pending });
705
+ return;
706
+ }
559
707
  throw new NoAccessError(`User "${options.userId}" has no keyring in vault "${vault}"`);
560
708
  }
561
709
  const targetKeyring = targetFound.file;
710
+ const callerRosterKey = requireRosterKey(callerKeyring, "revoke");
711
+ await assertRosterTagValid(targetKeyring, callerRosterKey, options.userId);
562
712
  if (!canRevoke(callerKeyring.role, targetKeyring.role)) {
563
713
  throw new PermissionDeniedError(
564
714
  `Role "${callerKeyring.role}" cannot revoke role "${targetKeyring.role}"`
@@ -568,7 +718,7 @@ async function revoke(store, vault, callerKeyring, options) {
568
718
  const usersToRevoke = [options.userId];
569
719
  const affectedCollections = new Set(Object.keys(targetKeyring.deks));
570
720
  if (targetKeyring.role === "admin") {
571
- const descendants = await findAdminDescendants(store, vault, options.userId);
721
+ const descendants = await findAdminDescendants(store, vault, options.userId, callerRosterKey);
572
722
  if (descendants.length > 0) {
573
723
  if (cascadeMode === "warn") {
574
724
  console.warn(
@@ -578,6 +728,7 @@ async function revoke(store, vault, callerKeyring, options) {
578
728
  for (const userId of descendants) {
579
729
  const descFound = await readKeyringFile(store, vault, userId);
580
730
  if (!descFound) continue;
731
+ await assertRosterTagValid(descFound.file, callerRosterKey, userId);
581
732
  usersToRevoke.push(userId);
582
733
  for (const c of Object.keys(descFound.file.deks)) affectedCollections.add(c);
583
734
  }
@@ -589,10 +740,105 @@ async function revoke(store, vault, callerKeyring, options) {
589
740
  await deleteUserEnvelope(store, vault, userId);
590
741
  await deleteUserVisibility(store, vault, userId);
591
742
  }
592
- if (options.rotateKeys !== false && affectedCollections.size > 0) {
593
- await rotateKeys(store, vault, callerKeyring, { collections: [...affectedCollections] });
743
+ affectedCollections.delete(ROSTER_KEY_ID);
744
+ affectedCollections.delete(BLOB_ADDRESS_KEY_ID);
745
+ if (affectedCollections.size > 0) {
746
+ const { unverified } = await rotateKeys(store, vault, callerKeyring, {
747
+ collections: [...affectedCollections]
748
+ });
749
+ if (unverified.length > 0) {
750
+ console.warn(
751
+ `[noy-db] revoke(${options.userId}): ${unverified.length} member(s) were SKIPPED by the rotation because their keyring failed roster authentication: ${unverified.map((u) => `${u.userId} (${u.reason})`).join(", ")}. The revocation itself completed. Those members cannot open the vault and did not receive the rotated keys; their keyring files need repair or removal (noy-db#1114).`
752
+ );
753
+ }
594
754
  }
595
755
  }
756
+ async function readKeyringForAudit(store, vault, userId) {
757
+ const envelope = await store.get(vault, "_keyring", userId);
758
+ if (!envelope) return void 0;
759
+ try {
760
+ return { file: parseKeyringEnvelope(envelope) };
761
+ } catch {
762
+ return { unparseable: true };
763
+ }
764
+ }
765
+ async function verifyRoster(store, vault, callerKeyring) {
766
+ const rosterKey = requireRosterKey(callerKeyring, "verifyRoster");
767
+ const unverified = [];
768
+ let checked = 0;
769
+ for (const userId of await store.list(vault, "_keyring")) {
770
+ const found = await readKeyringForAudit(store, vault, userId);
771
+ if (!found) continue;
772
+ checked += 1;
773
+ if ("unparseable" in found) {
774
+ unverified.push({ userId, reason: "unparseable" });
775
+ continue;
776
+ }
777
+ try {
778
+ await assertRosterTagValid(found.file, rosterKey, userId);
779
+ } catch (err) {
780
+ if (!(err instanceof KeyringTamperedError)) throw err;
781
+ unverified.push({ userId, reason: err.details.reason });
782
+ }
783
+ }
784
+ return { checked, unverified };
785
+ }
786
+ async function quarantineKeyring(store, vault, callerKeyring, userId) {
787
+ if (callerKeyring.role !== "owner") {
788
+ throw new PermissionDeniedError(
789
+ `Role "${callerKeyring.role}" cannot quarantine a keyring \u2014 quarantine is owner-only, because it removes a file whose own authority claims cannot be trusted.`
790
+ );
791
+ }
792
+ if (userId === callerKeyring.userId) {
793
+ throw new ValidationError(
794
+ "quarantineKeyring: refusing to quarantine the calling keyring. Removing your own keyring and rotating behind it would leave the vault with no reachable owner."
795
+ );
796
+ }
797
+ const rosterKey = requireRosterKey(callerKeyring, "quarantineKeyring");
798
+ const found = await readKeyringForAudit(store, vault, userId);
799
+ if (!found) {
800
+ const pending = [...callerKeyring.pendingDeks?.keys() ?? []];
801
+ if (pending.length > 0) {
802
+ const resumed = await rotateKeys(store, vault, callerKeyring, { collections: pending });
803
+ return {
804
+ userId,
805
+ reason: "roster-tag-mismatch",
806
+ rotated: pending,
807
+ needsRegrant: resumed.needsRegrant,
808
+ alsoUnverified: resumed.unverified
809
+ };
810
+ }
811
+ throw new NoAccessError(`No keyring found for user "${userId}" in vault "${vault}"`);
812
+ }
813
+ let reason = null;
814
+ if ("unparseable" in found) {
815
+ reason = "unparseable";
816
+ } else {
817
+ try {
818
+ await assertRosterTagValid(found.file, rosterKey, userId);
819
+ } catch (err) {
820
+ if (!(err instanceof KeyringTamperedError)) throw err;
821
+ reason = err.details.reason;
822
+ }
823
+ }
824
+ if (reason === null) {
825
+ throw new ValidationError(
826
+ `quarantineKeyring: the keyring for "${userId}" authenticates correctly, so it is not a quarantine case \u2014 use revoke() instead. Quarantine bypasses the role checks revoke performs, so it is restricted to files that genuinely fail authentication.`
827
+ );
828
+ }
829
+ await store.delete(vault, "_keyring", userId);
830
+ await deleteUserEnvelope(store, vault, userId);
831
+ await deleteUserVisibility(store, vault, userId);
832
+ const rotated = [...callerKeyring.deks.keys()].filter((c) => c !== ROSTER_KEY_ID);
833
+ const result = rotated.length > 0 ? await rotateKeys(store, vault, callerKeyring, { collections: rotated }) : { needsRegrant: [], unverified: [] };
834
+ return {
835
+ userId,
836
+ reason,
837
+ rotated,
838
+ needsRegrant: result.needsRegrant,
839
+ alsoUnverified: result.unverified
840
+ };
841
+ }
596
842
  async function updateKeyringIdentity(store, vault, callerKeyring, options) {
597
843
  if (options.role === void 0 && options.displayName === void 0 && options.permissions === void 0) {
598
844
  throw new ValidationError(
@@ -606,6 +852,8 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
606
852
  );
607
853
  }
608
854
  const target = found.file;
855
+ const rosterKey = requireRosterKey(callerKeyring, "updateKeyringIdentity");
856
+ await assertRosterTagValid(target, rosterKey, options.userId);
609
857
  if (!canUpdateRole(callerKeyring.role, target.role)) {
610
858
  throw new PermissionDeniedError(
611
859
  `Role "${callerKeyring.role}" cannot update a keyring with role "${target.role}"`
@@ -616,7 +864,7 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
616
864
  `Role "${callerKeyring.role}" cannot promote target to role "${options.role}"`
617
865
  );
618
866
  }
619
- const next = {
867
+ const edited = {
620
868
  ...target,
621
869
  ...options.role !== void 0 && { role: options.role },
622
870
  ...options.displayName !== void 0 && {
@@ -625,50 +873,130 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
625
873
  },
626
874
  ...options.permissions !== void 0 && { permissions: options.permissions }
627
875
  };
876
+ const next = { ...edited, roster_tag: await mintRosterTag(edited, rosterKey) };
628
877
  await writeKeyringFile(store, vault, options.userId, next);
629
878
  }
879
+ var HISTORY_COLLECTION = "_history";
880
+ var LEDGER_COLLECTION = "_ledger";
881
+ var LEDGER_DELTAS_COLLECTION = "_ledger_deltas";
882
+ var BLOB_COLLECTION = "_blob";
883
+ var BLOB_SLOTS_PREFIX = "_blob_slots_";
884
+ var BLOB_VERSIONS_PREFIX = "_blob_versions_";
885
+ var BLOB_INTENT_COLLECTION = "_blob_intent";
886
+ async function listOrEmpty(store, vault, collection) {
887
+ try {
888
+ return await store.list(vault, collection);
889
+ } catch {
890
+ return [];
891
+ }
892
+ }
893
+ async function derivedRefsFor(store, vault, rotated) {
894
+ const out = [];
895
+ if (!rotated.startsWith("_")) {
896
+ for (const id of await listOrEmpty(store, vault, HISTORY_COLLECTION)) {
897
+ if (id.startsWith(`${rotated}:`)) out.push({ collection: HISTORY_COLLECTION, id });
898
+ }
899
+ }
900
+ if (rotated === LEDGER_COLLECTION) {
901
+ for (const id of await listOrEmpty(store, vault, LEDGER_DELTAS_COLLECTION)) {
902
+ out.push({ collection: LEDGER_DELTAS_COLLECTION, id });
903
+ }
904
+ }
905
+ if (!rotated.startsWith("_")) {
906
+ for (const id of await listOrEmpty(store, vault, `${BLOB_SLOTS_PREFIX}${rotated}`)) {
907
+ out.push({ collection: `${BLOB_SLOTS_PREFIX}${rotated}`, id });
908
+ }
909
+ for (const id of await listOrEmpty(store, vault, `${BLOB_VERSIONS_PREFIX}${rotated}`)) {
910
+ out.push({ collection: `${BLOB_VERSIONS_PREFIX}${rotated}`, id });
911
+ }
912
+ for (const id of await listOrEmpty(store, vault, BLOB_INTENT_COLLECTION)) {
913
+ if (id.startsWith(`${rotated}::`)) out.push({ collection: BLOB_INTENT_COLLECTION, id });
914
+ }
915
+ }
916
+ return out;
917
+ }
630
918
  async function rotateKeys(store, vault, callerKeyring, opts) {
631
919
  const { collections } = opts;
920
+ if (collections.includes(BLOB_ADDRESS_KEY_ID)) {
921
+ throw new ValidationError(
922
+ `rotateKeys: "${BLOB_ADDRESS_KEY_ID}" is the vault's blob content-addressing root, not a collection, and cannot be rotated \u2014 every blob eTag is derived from it, so rotating it would invalidate every stored address at once. Remove it from \`collections\`.`
923
+ );
924
+ }
925
+ if (collections.includes(ROSTER_KEY_ID)) {
926
+ throw new ValidationError(
927
+ `rotateKeys: "${ROSTER_KEY_ID}" is the vault roster key, not a collection, and cannot be rotated \u2014 doing so would strip it from every other member and leave the vault unopenable. Remove it from \`collections\`.`
928
+ );
929
+ }
632
930
  if (callerKeyring.role === "custodian") {
633
931
  throw new PermissionDeniedError(
634
932
  "custodian cannot rotate keys (FR-6: re-key is an owner-only meta-capability; use the Deed owner)"
635
933
  );
636
934
  }
935
+ const callerRosterKey = requireRosterKey(callerKeyring, "rotateKeys");
637
936
  const newDeks = /* @__PURE__ */ new Map();
638
937
  for (const collName of collections) {
639
- newDeks.set(collName, await generateDEK());
938
+ newDeks.set(collName, callerKeyring.pendingDeks?.get(collName) ?? await generateDEK());
640
939
  }
940
+ const pending = callerKeyring.pendingDeks ?? /* @__PURE__ */ new Map();
941
+ callerKeyring.pendingDeks = pending;
942
+ for (const [collName, newDek] of newDeks) {
943
+ pending.set(collName, newDek);
944
+ }
945
+ await persistKeyring(store, vault, callerKeyring);
641
946
  for (const collName of collections) {
642
947
  const oldDek = callerKeyring.deks.get(collName);
643
948
  const newDek = newDeks.get(collName);
644
949
  if (!oldDek) continue;
645
- const ids = await store.list(vault, collName);
646
- for (const id of ids) {
647
- const envelope = await store.get(vault, collName, id);
950
+ if (collName === BLOB_COLLECTION || collName.startsWith(`${BLOB_COLLECTION}#`)) {
951
+ const others = [];
952
+ for (const [name, dek] of callerKeyring.deks) if (name !== collName) others.push(dek);
953
+ for (const [name, dek] of newDeks) if (name !== collName) others.push(dek);
954
+ await rekeyBlobSet(store, vault, oldDek, newDek, others);
955
+ }
956
+ const { collection: baseColl } = parseDekKey(collName);
957
+ const siblingSlotDeks = [];
958
+ for (const [name, dek] of callerKeyring.deks) if (name !== collName) siblingSlotDeks.push(dek);
959
+ for (const [name, dek] of newDeks) if (name !== collName) siblingSlotDeks.push(dek);
960
+ const refs = [
961
+ ...(await store.list(vault, baseColl)).map((id) => ({ collection: baseColl, id })),
962
+ ...await derivedRefsFor(store, vault, baseColl)
963
+ ];
964
+ for (const ref of refs) {
965
+ const { collection: refColl, id } = ref;
966
+ const envelope = await store.get(vault, refColl, id);
648
967
  if (!envelope || !envelope._iv) continue;
649
- const plaintext = await decrypt(envelope._iv, envelope._data, oldDek);
650
- const { iv, data } = await encrypt(plaintext, newDek);
651
- const newEnvelope = {
652
- _noydb: NOYDB_FORMAT_VERSION,
653
- _v: envelope._v,
654
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
655
- _iv: iv,
656
- _data: data
657
- };
658
- await store.put(vault, collName, id, newEnvelope);
968
+ let newEnvelope;
969
+ try {
970
+ newEnvelope = await rekeyEnvelopeIfNeeded({ collection: refColl, id }, envelope, oldDek, newDek);
971
+ } catch (err) {
972
+ if (await envelopeOpensUnderAny({ collection: refColl, id }, envelope, siblingSlotDeks)) {
973
+ continue;
974
+ }
975
+ throw err;
976
+ }
977
+ if (newEnvelope !== null) await store.put(vault, refColl, id, newEnvelope);
659
978
  }
660
979
  }
661
980
  for (const [collName, newDek] of newDeks) {
662
981
  callerKeyring.deks.set(collName, newDek);
982
+ callerKeyring.pendingDeks?.delete(collName);
663
983
  }
664
984
  await persistKeyring(store, vault, callerKeyring);
665
985
  const needsRegrant = [];
986
+ const unverified = [];
666
987
  const userIds = await store.list(vault, "_keyring");
667
988
  for (const userId of userIds) {
668
989
  if (userId === callerKeyring.userId) continue;
669
990
  const userFound = await readKeyringFile(store, vault, userId);
670
991
  if (!userFound) continue;
671
992
  const userKeyringFile = userFound.file;
993
+ try {
994
+ await assertRosterTagValid(userKeyringFile, callerRosterKey, userId);
995
+ } catch (err) {
996
+ if (!(err instanceof KeyringTamperedError)) throw err;
997
+ unverified.push({ userId, reason: err.details.reason });
998
+ continue;
999
+ }
672
1000
  const updatedDeks = { ...userKeyringFile.deks };
673
1001
  const updatedPermissions = { ...userKeyringFile.permissions };
674
1002
  for (const collName of collections) {
@@ -678,17 +1006,24 @@ async function rotateKeys(store, vault, callerKeyring, opts) {
678
1006
  delete updatedDeks[collName];
679
1007
  delete updatedPermissions[collName];
680
1008
  }
681
- const updatedKeyring = {
1009
+ const edited = {
682
1010
  ...userKeyringFile,
683
1011
  deks: updatedDeks,
684
1012
  permissions: updatedPermissions
685
1013
  };
1014
+ const updatedKeyring = {
1015
+ ...edited,
1016
+ roster_tag: await mintRosterTag(edited, callerRosterKey)
1017
+ };
686
1018
  await writeKeyringFile(store, vault, userId, updatedKeyring);
687
1019
  }
688
- return { needsRegrant };
1020
+ return { needsRegrant, unverified };
689
1021
  }
690
1022
  async function changeSecret(store, vault, keyring, opts) {
691
1023
  const existingFound = await readKeyringFile(store, vault, keyring.userId);
1024
+ if (existingFound) {
1025
+ await assertRosterTagValid(existingFound.file, requireRosterKey(keyring, "changeSecret"), keyring.userId);
1026
+ }
692
1027
  if (existingFound && existingFound.file.echo !== void 0) throw new EchoCeremonyRequiredError();
693
1028
  const { newSecret } = opts;
694
1029
  if (!opts.allowWeakSecret) {
@@ -700,18 +1035,28 @@ async function changeSecret(store, vault, keyring, opts) {
700
1035
  for (const [collName, dek] of keyring.deks) {
701
1036
  wrappedDeks[collName] = await wrapKey(dek, newKek);
702
1037
  }
1038
+ const rosterKey = requireRosterKey(keyring, "changeSecret");
703
1039
  const canary = await mintKeyringCanary(newKek);
704
- const keyringFile = {
705
- _noydb_keyring: NOYDB_KEYRING_VERSION,
1040
+ const existingAuthority = existingFound?.file;
1041
+ const keptGrantedBy = existingAuthority?.granted_by ?? keyring.userId;
1042
+ const authority = {
706
1043
  user_id: keyring.userId,
707
1044
  display_name: keyring.displayName,
708
1045
  role: keyring.role,
709
1046
  permissions: keyring.permissions,
710
- deks: wrappedDeks,
1047
+ granted_by: keptGrantedBy,
1048
+ ...existingAuthority?.expires_at !== void 0 && { expires_at: existingAuthority.expires_at },
1049
+ ...existingAuthority?.export_capability !== void 0 && { export_capability: existingAuthority.export_capability },
1050
+ ...existingAuthority?.import_capability !== void 0 && { import_capability: existingAuthority.import_capability }
1051
+ };
1052
+ const authorityWithDeks = { ...authority, deks: wrappedDeks };
1053
+ const keyringFile = {
1054
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
1055
+ ...authorityWithDeks,
711
1056
  salt: bufferToBase64(newSalt),
712
1057
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
713
- granted_by: keyring.userId,
714
- canary
1058
+ canary,
1059
+ roster_tag: await mintRosterTag(authorityWithDeks, rosterKey)
715
1060
  };
716
1061
  await writeKeyringFile(store, vault, keyring.userId, keyringFile);
717
1062
  return {
@@ -738,6 +1083,7 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
738
1083
  "buildRecipientKeyringFile: caller keyring has no KEK \u2014 tier-2 wrap-DEKs and tier-3 PIN-resume sessions cannot create bundle recipients. Re-authenticate at tier 1 (secret) before building a bundle."
739
1084
  );
740
1085
  }
1086
+ const rosterKey = requireRosterKey(callerKeyring, "buildRecipientKeyringFile");
741
1087
  const role = recipient.role ?? "viewer";
742
1088
  const permissions = resolvePermissions(role, recipient.permissions);
743
1089
  const newSalt = generateSalt();
@@ -767,20 +1113,26 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
767
1113
  }
768
1114
  }
769
1115
  const canary = await mintKeyringCanary(newKek);
770
- return {
771
- _noydb_keyring: NOYDB_KEYRING_VERSION,
1116
+ const authority = {
772
1117
  user_id: recipient.id,
773
1118
  display_name: recipient.displayName ?? recipient.id,
774
1119
  role,
775
1120
  permissions,
776
- deks: wrappedDeks,
777
- salt: bufferToBase64(newSalt),
778
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
779
1121
  granted_by: callerKeyring.userId,
780
- canary,
781
1122
  ...recipient.exportCapability !== void 0 ? { export_capability: recipient.exportCapability } : {},
782
1123
  ...recipient.importCapability !== void 0 ? { import_capability: recipient.importCapability } : {},
783
- ...recipient.expiresAt !== void 0 ? { expires_at: recipient.expiresAt } : {},
1124
+ ...recipient.expiresAt !== void 0 ? { expires_at: recipient.expiresAt } : {}
1125
+ };
1126
+ const authorityWithDeks = { ...authority, deks: wrappedDeks };
1127
+ return {
1128
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
1129
+ ...authorityWithDeks,
1130
+ salt: bufferToBase64(newSalt),
1131
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
1132
+ canary,
1133
+ // The recipient's copy of the roster key rode in via the `_`-prefix
1134
+ // propagation loop above, so the slot verifies on first unlock.
1135
+ roster_tag: await mintRosterTag(authorityWithDeks, rosterKey),
784
1136
  // The presence of this block is what makes the recipient's slot an
785
1137
  // echo keyring — mirrors `createOwnerKeyring`'s same dance so the
786
1138
  // ceremony (prompt → echo → key) travels with the pod, not just a
@@ -908,28 +1260,44 @@ async function persistKeyring(store, vault, keyring) {
908
1260
  "persistKeyring: keyring.kek is null \u2014 cannot wrap DEKs without the KEK. This typically means the keyring was opened via tier-3 PIN resume, session restore, or a wrap-DEKs tier-2 unlock. Re-authenticate at tier 1 (secret) before persisting."
909
1261
  );
910
1262
  }
1263
+ const rosterKey = requireRosterKey(keyring, "persistKeyring");
911
1264
  const existingFound = await readKeyringFile(store, vault, keyring.userId);
1265
+ if (existingFound) await assertRosterTagValid(existingFound.file, rosterKey, keyring.userId);
912
1266
  const existingEcho = existingFound?.file.echo;
913
1267
  const existingGrantedBy = existingFound?.file.granted_by;
914
1268
  const existingCreatedAt = existingFound?.file.created_at;
1269
+ const existingExpiresAt = existingFound?.file.expires_at;
915
1270
  const wrappedDeks = {};
916
1271
  for (const [collName, dek] of keyring.deks) {
917
1272
  wrappedDeks[collName] = await wrapKey(dek, keyring.kek);
918
1273
  }
1274
+ const wrappedPending = {};
1275
+ for (const [collName, dek] of keyring.pendingDeks ?? []) {
1276
+ wrappedPending[collName] = await wrapKey(dek, keyring.kek);
1277
+ }
919
1278
  const canary = await mintKeyringCanary(keyring.kek);
920
- const keyringFile = {
921
- _noydb_keyring: NOYDB_KEYRING_VERSION,
1279
+ const authority = {
922
1280
  user_id: keyring.userId,
923
1281
  display_name: keyring.displayName,
924
1282
  role: keyring.role,
925
1283
  permissions: keyring.permissions,
1284
+ granted_by: existingGrantedBy ?? keyring.userId,
1285
+ ...existingExpiresAt !== void 0 && { expires_at: existingExpiresAt },
1286
+ ...keyring.exportCapability !== void 0 && { export_capability: keyring.exportCapability },
1287
+ ...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability }
1288
+ };
1289
+ const authorityWithDeks = {
1290
+ ...authority,
926
1291
  deks: wrappedDeks,
1292
+ ...Object.keys(wrappedPending).length > 0 ? { pending_deks: wrappedPending } : {}
1293
+ };
1294
+ const keyringFile = {
1295
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
1296
+ ...authorityWithDeks,
927
1297
  salt: bufferToBase64(keyring.salt),
928
1298
  created_at: existingCreatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
929
- granted_by: existingGrantedBy ?? keyring.userId,
930
1299
  canary,
931
- ...keyring.exportCapability !== void 0 && { export_capability: keyring.exportCapability },
932
- ...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability },
1300
+ roster_tag: await mintRosterTag(authorityWithDeks, rosterKey),
933
1301
  ...keyring.authenticators.length > 0 && { authenticators: keyring.authenticators },
934
1302
  ...keyring.policy !== void 0 && { policy: keyring.policy },
935
1303
  ...existingEcho !== void 0 && { echo: existingEcho }
@@ -974,17 +1342,17 @@ function resolvePermissions(role, explicit) {
974
1342
  return explicit ?? {};
975
1343
  }
976
1344
  async function writeKeyringFile(store, vault, userId, keyringFile) {
977
- const envelope = {
978
- _noydb: 1,
979
- _v: 1,
980
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
981
- _iv: "",
982
- _data: JSON.stringify(keyringFile)
983
- };
1345
+ const envelope = buildRecordEnvelope(
1346
+ { collection: "_keyring", id: userId, version: 1 },
1347
+ { iv: "", data: JSON.stringify(keyringFile) }
1348
+ );
984
1349
  await store.put(vault, "_keyring", userId, envelope);
985
1350
  }
986
1351
 
987
1352
  export {
1353
+ mintRosterTag,
1354
+ assertRosterAuthenticated,
1355
+ assertRosterTagValid,
988
1356
  WeakSecretError,
989
1357
  validateSecret,
990
1358
  assertStrongSecret,
@@ -1001,6 +1369,7 @@ export {
1001
1369
  BROKER_COLLECTION,
1002
1370
  isSecretBearingReservedCollection,
1003
1371
  mintKeyringCanary,
1372
+ requireRosterKey,
1004
1373
  deriveKekForKeyring,
1005
1374
  readKeyringFile,
1006
1375
  assertKeyringNotExpired,
@@ -1009,6 +1378,8 @@ export {
1009
1378
  createOwnerKeyring,
1010
1379
  grant,
1011
1380
  revoke,
1381
+ verifyRoster,
1382
+ quarantineKeyring,
1012
1383
  updateKeyringIdentity,
1013
1384
  rotateKeys,
1014
1385
  changeSecret,
@@ -1024,4 +1395,4 @@ export {
1024
1395
  hasImportCapability,
1025
1396
  evaluateImportCapability
1026
1397
  };
1027
- //# sourceMappingURL=chunk-JRKZTEZZ.js.map
1398
+ //# sourceMappingURL=chunk-J6I5VR6P.js.map