@noy-db/hub 0.6.0-pre.2 → 0.6.0-pre.21

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 (499) hide show
  1. package/CHANGELOG.md +5234 -0
  2. package/codemods/0.4.0-pre.json +699 -0
  3. package/codemods/0.6.0-pre.json +211 -0
  4. package/dist/api-EYNT6KGS.js +20 -0
  5. package/dist/attestation/index.js +19 -19
  6. package/dist/{backup-YYBE5RPC.js → backup-FSC36VWC.js} +36 -18
  7. package/dist/backup-FSC36VWC.js.map +1 -0
  8. package/dist/blobs/index.js +13 -13
  9. package/dist/broker/index.js +12 -12
  10. package/dist/cargo/index.js +33 -33
  11. package/dist/{chunk-35EBKWQU.js → chunk-2LFOMUEA.js} +4 -4
  12. package/dist/{chunk-OF3L2SD6.js → chunk-32B7VCKU.js} +2 -2
  13. package/dist/{chunk-MSWYQLMQ.js → chunk-3B6S6RSI.js} +2 -2
  14. package/dist/{chunk-PJNTR2NV.js → chunk-3ENNPCIW.js} +7 -10
  15. package/dist/chunk-3ENNPCIW.js.map +1 -0
  16. package/dist/{chunk-7F4DBNSP.js → chunk-3O72ZD2E.js} +3 -3
  17. package/dist/{chunk-FNSMZQS4.js → chunk-3PMFMUFX.js} +2 -2
  18. package/dist/{chunk-WQDTCJ6W.js → chunk-45YHJ3GV.js} +11 -3
  19. package/dist/{chunk-WQDTCJ6W.js.map → chunk-45YHJ3GV.js.map} +1 -1
  20. package/dist/{chunk-NIVEDYJZ.js → chunk-4A34KWOU.js} +31 -49
  21. package/dist/chunk-4A34KWOU.js.map +1 -0
  22. package/dist/{chunk-ZJADGNYF.js → chunk-4AOERLTV.js} +1 -1
  23. package/dist/chunk-4AOERLTV.js.map +1 -0
  24. package/dist/{chunk-EDXAQ6JI.js → chunk-4N5Q23KV.js} +87 -34
  25. package/dist/chunk-4N5Q23KV.js.map +1 -0
  26. package/dist/{chunk-VV4NSB7P.js → chunk-4OQZNXQG.js} +15 -24
  27. package/dist/chunk-4OQZNXQG.js.map +1 -0
  28. package/dist/{chunk-FAXZQ5UF.js → chunk-4RAS6HN6.js} +2 -2
  29. package/dist/{chunk-C2OOEM57.js → chunk-4YL2LMXK.js} +92 -35
  30. package/dist/chunk-4YL2LMXK.js.map +1 -0
  31. package/dist/{chunk-EP7VPR7Y.js → chunk-5Q7AJ7DN.js} +11 -8
  32. package/dist/chunk-5Q7AJ7DN.js.map +1 -0
  33. package/dist/{chunk-VMCCGUQB.js → chunk-6JPGVL7X.js} +2 -2
  34. package/dist/{chunk-PRDRGL73.js → chunk-6KTVJ5ZZ.js} +17 -14
  35. package/dist/chunk-6KTVJ5ZZ.js.map +1 -0
  36. package/dist/{chunk-4MC5JRRX.js → chunk-6WO62AJW.js} +14 -15
  37. package/dist/chunk-6WO62AJW.js.map +1 -0
  38. package/dist/{chunk-WLYIROCJ.js → chunk-6YD5SYQS.js} +2 -2
  39. package/dist/{chunk-YAWK6AWF.js → chunk-7KAE7CQ2.js} +2 -2
  40. package/dist/{chunk-MWKDOHEF.js → chunk-7LNE2N2V.js} +17 -10
  41. package/dist/chunk-7LNE2N2V.js.map +1 -0
  42. package/dist/{chunk-5PR33AC4.js → chunk-7PJ3BKHS.js} +20 -17
  43. package/dist/chunk-7PJ3BKHS.js.map +1 -0
  44. package/dist/{chunk-JXIDGFKS.js → chunk-A4TOGAUW.js} +4 -2
  45. package/dist/chunk-A4TOGAUW.js.map +1 -0
  46. package/dist/{chunk-VCETL77O.js → chunk-AU3AUJIZ.js} +27 -7
  47. package/dist/chunk-AU3AUJIZ.js.map +1 -0
  48. package/dist/{chunk-NKH7GSIH.js → chunk-AUPSEJ7O.js} +4 -4
  49. package/dist/{chunk-CCZJJ4I2.js → chunk-B6IYATME.js} +4 -4
  50. package/dist/{chunk-WYZ5MYUA.js → chunk-BAHOV5I5.js} +44 -42
  51. package/dist/chunk-BAHOV5I5.js.map +1 -0
  52. package/dist/{chunk-G5APARUV.js → chunk-BEZDLIDG.js} +13 -15
  53. package/dist/chunk-BEZDLIDG.js.map +1 -0
  54. package/dist/{chunk-ERB6NDDW.js → chunk-CAGI4LLJ.js} +427 -72
  55. package/dist/chunk-CAGI4LLJ.js.map +1 -0
  56. package/dist/{chunk-4UVDI4XE.js → chunk-CLAHBPOI.js} +4 -4
  57. package/dist/{chunk-HNGSV2YC.js → chunk-CWM4GQD2.js} +39 -10
  58. package/dist/chunk-CWM4GQD2.js.map +1 -0
  59. package/dist/{chunk-HVQGCKY4.js → chunk-D3R7Q4QK.js} +2 -2
  60. package/dist/{chunk-I4F6PKUK.js → chunk-DIZ6HM7Q.js} +16 -17
  61. package/dist/chunk-DIZ6HM7Q.js.map +1 -0
  62. package/dist/{chunk-ZNACADFG.js → chunk-DVRZYAGT.js} +4 -4
  63. package/dist/chunk-DVRZYAGT.js.map +1 -0
  64. package/dist/{chunk-P7OG2ETF.js → chunk-DXKLLKME.js} +3 -3
  65. package/dist/{chunk-TDJPS3T2.js → chunk-E5SC6ENM.js} +24 -19
  66. package/dist/chunk-E5SC6ENM.js.map +1 -0
  67. package/dist/{chunk-26JMGSSC.js → chunk-EES37S2V.js} +4 -4
  68. package/dist/chunk-EES37S2V.js.map +1 -0
  69. package/dist/{chunk-JIUVJ4M7.js → chunk-EKEIEJL6.js} +2 -2
  70. package/dist/{chunk-GASWQ3XG.js → chunk-ESKIAXO2.js} +15 -16
  71. package/dist/chunk-ESKIAXO2.js.map +1 -0
  72. package/dist/{chunk-TOAIYQJR.js → chunk-FFSQFH6D.js} +7 -10
  73. package/dist/chunk-FFSQFH6D.js.map +1 -0
  74. package/dist/{chunk-DG27IUVI.js → chunk-FS7NTGCJ.js} +2 -2
  75. package/dist/{chunk-V5KYPBUR.js → chunk-FW7NGYLT.js} +11 -14
  76. package/dist/chunk-FW7NGYLT.js.map +1 -0
  77. package/dist/{chunk-52QYGZHH.js → chunk-GBBDFE6C.js} +2 -2
  78. package/dist/chunk-GBBDFE6C.js.map +1 -0
  79. package/dist/{chunk-HXGUIPQB.js → chunk-GQXFOAYZ.js} +3 -3
  80. package/dist/{chunk-BTVYJWSN.js → chunk-GTHVPVAR.js} +10 -10
  81. package/dist/{chunk-DMQCLZ4Z.js → chunk-GWO2IDAB.js} +2 -2
  82. package/dist/{chunk-6HFM7EIT.js → chunk-H3G4I3PN.js} +2 -2
  83. package/dist/chunk-HK67PX4J.js +27 -0
  84. package/dist/chunk-HK67PX4J.js.map +1 -0
  85. package/dist/{chunk-E7OBCBXS.js → chunk-HQSPF5IY.js} +22 -18
  86. package/dist/chunk-HQSPF5IY.js.map +1 -0
  87. package/dist/{chunk-VJFMXYKP.js → chunk-HR5R5JOM.js} +2 -2
  88. package/dist/{chunk-FDX7OWWI.js → chunk-HYYVT2KZ.js} +2 -2
  89. package/dist/{chunk-JRCYZEHU.js → chunk-I6QKB7MT.js} +2 -2
  90. package/dist/{chunk-MZ5ZFO2D.js → chunk-ICDHUUMQ.js} +84 -112
  91. package/dist/chunk-ICDHUUMQ.js.map +1 -0
  92. package/dist/{chunk-W5W6KHN2.js → chunk-IGNJOJEP.js} +1 -1
  93. package/dist/chunk-IGNJOJEP.js.map +1 -0
  94. package/dist/{chunk-KUDZVJKQ.js → chunk-IJ46VCWD.js} +2 -2
  95. package/dist/{chunk-4HDYQL3J.js → chunk-INMHEMXX.js} +2 -2
  96. package/dist/{chunk-66W5OOCX.js → chunk-JLYQQNY4.js} +52 -65
  97. package/dist/chunk-JLYQQNY4.js.map +1 -0
  98. package/dist/{chunk-UHXCLPGC.js → chunk-JNMTN5D5.js} +2 -2
  99. package/dist/chunk-JNMTN5D5.js.map +1 -0
  100. package/dist/{chunk-I4ZAO7F7.js → chunk-JWMBG5S3.js} +7 -11
  101. package/dist/chunk-JWMBG5S3.js.map +1 -0
  102. package/dist/chunk-KRVS7YWY.js +123 -0
  103. package/dist/chunk-KRVS7YWY.js.map +1 -0
  104. package/dist/{chunk-AR2JDFAE.js → chunk-KV6JM2IA.js} +2 -2
  105. package/dist/{chunk-PVU7LW5L.js → chunk-KX57HODD.js} +6 -6
  106. package/dist/{chunk-PY7CI6L5.js → chunk-LGFTOU27.js} +553 -275
  107. package/dist/chunk-LGFTOU27.js.map +1 -0
  108. package/dist/chunk-LN7P6MZB.js +190 -0
  109. package/dist/chunk-LN7P6MZB.js.map +1 -0
  110. package/dist/{chunk-UIBGXRDD.js → chunk-LX46B56Q.js} +25 -31
  111. package/dist/chunk-LX46B56Q.js.map +1 -0
  112. package/dist/{chunk-CEJWMBXO.js → chunk-LXECF2ZR.js} +2 -2
  113. package/dist/{chunk-7VHY5E3X.js → chunk-MSLPDFK2.js} +7 -7
  114. package/dist/{chunk-I6SDX7WX.js → chunk-MZTUTEHP.js} +16 -22
  115. package/dist/chunk-MZTUTEHP.js.map +1 -0
  116. package/dist/{chunk-VRA4Q7DA.js → chunk-NCCEDL2K.js} +55 -48
  117. package/dist/chunk-NCCEDL2K.js.map +1 -0
  118. package/dist/{chunk-HNHCQ556.js → chunk-NR2SUJ3H.js} +2 -2
  119. package/dist/{chunk-AHLJSVRL.js → chunk-NW4TY6VC.js} +5 -7
  120. package/dist/chunk-NW4TY6VC.js.map +1 -0
  121. package/dist/{chunk-RKKORO4M.js → chunk-O742QBZZ.js} +10 -10
  122. package/dist/chunk-O742QBZZ.js.map +1 -0
  123. package/dist/{chunk-VN45YLWM.js → chunk-OHOKBMTM.js} +343 -132
  124. package/dist/chunk-OHOKBMTM.js.map +1 -0
  125. package/dist/{chunk-W3TVWF6N.js → chunk-OIBEJN5H.js} +2 -2
  126. package/dist/{chunk-QD724WDJ.js → chunk-OMYCSTXL.js} +4 -4
  127. package/dist/{chunk-Z45YRGFO.js → chunk-ONVD6GBY.js} +2 -2
  128. package/dist/{chunk-TAGFGKHP.js → chunk-OTHHO3KI.js} +7 -10
  129. package/dist/chunk-OTHHO3KI.js.map +1 -0
  130. package/dist/{chunk-3737A6EA.js → chunk-P2MR5UNE.js} +75 -18
  131. package/dist/chunk-P2MR5UNE.js.map +1 -0
  132. package/dist/chunk-PENPHA45.js +66 -0
  133. package/dist/chunk-PENPHA45.js.map +1 -0
  134. package/dist/{chunk-3FSX6LDG.js → chunk-PPCOKVFA.js} +8 -3
  135. package/dist/{chunk-3FSX6LDG.js.map → chunk-PPCOKVFA.js.map} +1 -1
  136. package/dist/{chunk-VOQW5LAM.js → chunk-QSIEL6TL.js} +102 -71
  137. package/dist/chunk-QSIEL6TL.js.map +1 -0
  138. package/dist/chunk-RFO4UXKQ.js +201 -0
  139. package/dist/chunk-RFO4UXKQ.js.map +1 -0
  140. package/dist/{chunk-KCQX65FU.js → chunk-RKCRPPNB.js} +3 -3
  141. package/dist/{chunk-7TSYBD3E.js → chunk-RSXQNFGO.js} +2 -2
  142. package/dist/{chunk-SZ2K3MHL.js → chunk-S56VYR4Q.js} +8 -11
  143. package/dist/{chunk-SZ2K3MHL.js.map → chunk-S56VYR4Q.js.map} +1 -1
  144. package/dist/{chunk-7NXHMW6Y.js → chunk-SYS46BL7.js} +2 -2
  145. package/dist/{chunk-55LKVALY.js → chunk-T7IR4N2S.js} +1 -3
  146. package/dist/chunk-T7IR4N2S.js.map +1 -0
  147. package/dist/{chunk-A67356IR.js → chunk-T7JGWTES.js} +33 -8
  148. package/dist/chunk-T7JGWTES.js.map +1 -0
  149. package/dist/{chunk-WHJKIZS7.js → chunk-TDSN2O2V.js} +27 -149
  150. package/dist/chunk-TDSN2O2V.js.map +1 -0
  151. package/dist/{chunk-747LRSOO.js → chunk-TMBSPLUG.js} +2 -2
  152. package/dist/{chunk-LWSJG3LI.js → chunk-TQPASLY4.js} +7 -3
  153. package/dist/chunk-TQPASLY4.js.map +1 -0
  154. package/dist/{chunk-WKPJXWKR.js → chunk-TX5MKAHA.js} +3 -3
  155. package/dist/{chunk-HFQ6LGIO.js → chunk-U27Y4DJZ.js} +12 -10
  156. package/dist/{chunk-HFQ6LGIO.js.map → chunk-U27Y4DJZ.js.map} +1 -1
  157. package/dist/{chunk-E6427JU2.js → chunk-VVSOPRUE.js} +18 -2
  158. package/dist/chunk-VVSOPRUE.js.map +1 -0
  159. package/dist/{chunk-7G6VDWXB.js → chunk-WVE6YKMX.js} +3 -3
  160. package/dist/chunk-WVE6YKMX.js.map +1 -0
  161. package/dist/{chunk-GLKDWWNO.js → chunk-YALD3SST.js} +16 -14
  162. package/dist/chunk-YALD3SST.js.map +1 -0
  163. package/dist/{chunk-NJDQ4YSV.js → chunk-YKXZHUOW.js} +4 -4
  164. package/dist/{chunk-VW6ZZGCG.js → chunk-ZLHHE5XZ.js} +3 -3
  165. package/dist/{chunk-FUHKW252.js → chunk-ZZXLB6AS.js} +2 -2
  166. package/dist/classified/index.js +5 -5
  167. package/dist/{classified-marker-FTOX7Q5L.js → classified-marker-NA6HUV33.js} +3 -3
  168. package/dist/collection-facade-VBM35WAH.js +45 -0
  169. package/dist/{computed-6G5M3UPH.js → computed-GSTLWSY5.js} +3 -3
  170. package/dist/consent/index.js +10 -10
  171. package/dist/cover/index.js +10 -3
  172. package/dist/custody/index.js +11 -11
  173. package/dist/{dead-filter-ENRIOQIE.js → dead-filter-UALP5DJX.js} +2 -2
  174. package/dist/debug/index.js +1 -1
  175. package/dist/{delegation-SSEPLXXO.js → delegation-JLNXM7KH.js} +24 -23
  176. package/dist/delegation-JLNXM7KH.js.map +1 -0
  177. package/dist/derivations/index.js +12 -12
  178. package/dist/derive-YUO3TQYN.js +22 -0
  179. package/dist/directory/index.js +10 -10
  180. package/dist/{dispatch-RE74SH6L.js → dispatch-4ZHLSFZQ.js} +5 -5
  181. package/dist/{dispatch-BDVGYC6X.js → dispatch-77CB75KB.js} +18 -18
  182. package/dist/{enclave-52VUXNRL.js → enclave-O4NOROCJ.js} +27 -9
  183. package/dist/executor-CZDXBXKE.js +29 -0
  184. package/dist/executor-DVJAWN3B.js +9 -0
  185. package/dist/executor-VRLG4TQJ.js +9 -0
  186. package/dist/export-accessible-UXSMG7AB.js +23 -0
  187. package/dist/extract-partition-TVY7AMKH.js +38 -0
  188. package/dist/{fanout-sidecar-DTBIDOWF.js → fanout-sidecar-NLQ7QAYW.js} +18 -27
  189. package/dist/fanout-sidecar-NLQ7QAYW.js.map +1 -0
  190. package/dist/find-WSARMJBW.js +11 -0
  191. package/dist/forget/index.js +9 -9
  192. package/dist/guards/index.js +7 -7
  193. package/dist/history/index.js +23 -20
  194. package/dist/history/index.js.map +1 -1
  195. package/dist/i18n/index.js +14 -14
  196. package/dist/index.d.ts +33 -15
  197. package/dist/index.js +187 -191
  198. package/dist/index.js.map +1 -1
  199. package/dist/indexing/index.js +2 -2
  200. package/dist/introspection/index.js +11 -11
  201. package/dist/issue-3JNJNPT3.js +19 -0
  202. package/dist/kernel/constants.d.ts +18 -0
  203. package/dist/kernel/enclave/crypto.d.ts +24 -4
  204. package/dist/kernel/enclave/index.d.ts +9 -2
  205. package/dist/kernel/enclave/record-aad.d.ts +131 -0
  206. package/dist/kernel/enclave/record-envelope.d.ts +76 -0
  207. package/dist/kernel/enclave/record-keys/envelope-body.d.ts +34 -2
  208. package/dist/kernel/enclave/record-keys/lifecycle.d.ts +22 -5
  209. package/dist/kernel/enclave/record-keys/record-codec.d.ts +47 -14
  210. package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +88 -0
  211. package/dist/kernel/enclave/record-keys/rekey.d.ts +68 -0
  212. package/dist/kernel/enclave/record-keys/tombstone.d.ts +3 -2
  213. package/dist/kernel/errors.d.ts +71 -12
  214. package/dist/kernel/merge-authority.d.ts +25 -0
  215. package/dist/kernel/noydb.d.ts +27 -4
  216. package/dist/kernel/query/builder.d.ts +15 -2
  217. package/dist/kernel/query/join.d.ts +27 -0
  218. package/dist/kernel/sync-policy.d.ts +0 -2
  219. package/dist/kernel/tier-visibility.d.ts +15 -0
  220. package/dist/kernel/types.d.ts +145 -12
  221. package/dist/kernel/vault.d.ts +22 -18
  222. package/dist/kernel/via/index.d.ts +11 -2
  223. package/dist/{ledger-4WJL66BW.js → ledger-MVPNG5OS.js} +11 -11
  224. package/dist/liberate-4LOJOLDT.js +24 -0
  225. package/dist/{link-set-N4ZPV6E6.js → link-set-3POMQR3I.js} +11 -11
  226. package/dist/{managed-secret-OTHS64IF.js → managed-secret-IARIJJ7Q.js} +11 -3
  227. package/dist/materialized-views/index.js +22 -21
  228. package/dist/migrate-cek-XM2ETTZW.js +19 -0
  229. package/dist/migrate-cek-XM2ETTZW.js.map +1 -0
  230. package/dist/money/index.js +12 -8
  231. package/dist/noydb-IRILUDED.js +84 -0
  232. package/dist/overlay-views/index.js +4 -4
  233. package/dist/periods/index.js +22 -12
  234. package/dist/pod/index.js +75 -86
  235. package/dist/pod-handle-MELERCKU.js +41 -0
  236. package/dist/pod-handle-MELERCKU.js.map +1 -0
  237. package/dist/policy/index.js +11 -4
  238. package/dist/port/to/index.d.ts +4 -4
  239. package/dist/port/to/locator.d.ts +54 -8
  240. package/dist/port/with/i18n-strategy.d.ts +4 -1
  241. package/dist/port/with/lookup-strategy.d.ts +4 -1
  242. package/dist/port/with/merge-authority.d.ts +52 -0
  243. package/dist/port/with/service-bus.d.ts +0 -4
  244. package/dist/port/with/strategies.d.ts +2 -0
  245. package/dist/port/with/team-strategy.d.ts +5 -1
  246. package/dist/portability/index.js +8 -8
  247. package/dist/{post-register-ZAG2CAIY.js → post-register-Z5T7FY45.js} +6 -6
  248. package/dist/query/index.js +3 -3
  249. package/dist/reduce/index.js +4 -4
  250. package/dist/register-UZ37QPDB.js +32 -0
  251. package/dist/registry-ETJNA7PO.js +9 -0
  252. package/dist/registry-F7U6KSLX.js +9 -0
  253. package/dist/registry-L4ELKX6C.js +18 -0
  254. package/dist/registry-SDT5LIGO.js +19 -0
  255. package/dist/request-withdrawal-PVFEYX57.js +31 -0
  256. package/dist/{reveal-CMOJY4VO.js → reveal-DQ2E3QAZ.js} +6 -6
  257. package/dist/revoke-YXY6SHUH.js +24 -0
  258. package/dist/satellites/index.js +1 -1
  259. package/dist/schema-update/index.js +2 -2
  260. package/dist/sealed-record/index.js +20 -13
  261. package/dist/sealed-record/index.js.map +1 -1
  262. package/dist/search/index.js +4 -4
  263. package/dist/{seed-UK4QRIUN.js → seed-DZ4ORU5N.js} +19 -22
  264. package/dist/seed-DZ4ORU5N.js.map +1 -0
  265. package/dist/sequence/index.js +9 -9
  266. package/dist/session/index.js +10 -10
  267. package/dist/shadow/index.js +2 -2
  268. package/dist/share-link/index.js +1 -1
  269. package/dist/signer-Q7XQBMPA.js +25 -0
  270. package/dist/snapshots/index.js +10 -10
  271. package/dist/snapshots/index.js.map +1 -1
  272. package/dist/{stale-ZWXSOWJI.js → stale-NI7SAXAZ.js} +10 -10
  273. package/dist/storage-3VNGSHXS.js +23 -0
  274. package/dist/{store-coordination-provider-PB6QZ57W.js → store-coordination-provider-TYKNR3OU.js} +18 -12
  275. package/dist/store-coordination-provider-TYKNR3OU.js.map +1 -0
  276. package/dist/sync/index.js +11 -11
  277. package/dist/team/index.js +15 -14
  278. package/dist/tiers/index.js +11 -10
  279. package/dist/to/index.js +17 -9
  280. package/dist/to/index.js.map +1 -1
  281. package/dist/transactions/index.js +4 -4
  282. package/dist/{ulid-PTAIMDG4.js → ulid-VQW6ERQN.js} +2 -2
  283. package/dist/util/index.js +1 -1
  284. package/dist/vault-head/index.js +115 -0
  285. package/dist/vault-head/index.js.map +1 -0
  286. package/dist/{verify-RO5RTZOJ.js → verify-6J2YLKSO.js} +5 -5
  287. package/dist/via/lookup/handle.d.ts +4 -1
  288. package/dist/via/money/exact.d.ts +44 -0
  289. package/dist/via/money/index.d.ts +2 -0
  290. package/dist/via/money/normalize.d.ts +20 -0
  291. package/dist/walk-WRA7RAPY.js +21 -0
  292. package/dist/with-audit/guards/immutable-guard.d.ts +14 -0
  293. package/dist/with-audit/periods/active.d.ts +28 -1
  294. package/dist/with-audit/periods/index.d.ts +4 -2
  295. package/dist/with-audit/periods/periods.d.ts +161 -6
  296. package/dist/with-audit/periods/strategy.d.ts +14 -5
  297. package/dist/with-audit/periods/vault-facade.d.ts +89 -10
  298. package/dist/with-audit/periods/window.d.ts +10 -0
  299. package/dist/with-audit/sealed-record/index.d.ts +13 -0
  300. package/dist/with-audit/tiers/index.d.ts +17 -2
  301. package/dist/with-cargo/adopt-partition.d.ts +1 -1
  302. package/dist/with-cargo/extract-partition.d.ts +1 -1
  303. package/dist/with-cargo/index.d.ts +2 -2
  304. package/dist/with-commit/history/history.d.ts +26 -0
  305. package/dist/with-commit/history/index.d.ts +2 -1
  306. package/dist/with-commit/history/ledger/store.d.ts +7 -1
  307. package/dist/with-commit/history/strategy.d.ts +11 -1
  308. package/dist/with-commit/numbering/index.d.ts +0 -5
  309. package/dist/with-commit/vault-head/active.d.ts +2 -0
  310. package/dist/with-commit/vault-head/head.d.ts +28 -0
  311. package/dist/with-commit/vault-head/index.d.ts +10 -0
  312. package/dist/with-commit/vault-head/strategy.d.ts +127 -0
  313. package/dist/with-commit/vault-head/verify.d.ts +90 -0
  314. package/dist/with-formula/materialized-views/types.d.ts +52 -0
  315. package/dist/with-party/directory/cover/storage.d.ts +0 -8
  316. package/dist/with-party/directory/storage.d.ts +0 -11
  317. package/dist/with-party/directory/visibility.d.ts +0 -21
  318. package/dist/with-party/policy/storage.d.ts +0 -10
  319. package/dist/with-party/team/index.d.ts +1 -1
  320. package/dist/with-party/team/keyring.d.ts +149 -3
  321. package/dist/with-party/team/noydb-facade.d.ts +5 -1
  322. package/dist/with-party/team/roster-tag.d.ts +101 -0
  323. package/dist/with-party/team/tiers.d.ts +2 -1
  324. package/dist/with-pod/format.d.ts +10 -12
  325. package/dist/with-pod/index.d.ts +10 -10
  326. package/dist/with-pod/open.d.ts +2 -2
  327. package/dist/with-pod/pod-handle.d.ts +19 -0
  328. package/dist/with-pod/pod-store.d.ts +0 -8
  329. package/dist/with-pod/{bundle.d.ts → pod.d.ts} +26 -19
  330. package/dist/with-pod/ulid.d.ts +1 -1
  331. package/dist/with-shape/blobs/blob-set.d.ts +2 -2
  332. package/dist/with-shape/introspection/index.d.ts +1 -0
  333. package/dist/with-sync/engine.d.ts +54 -5
  334. package/dist/with-sync/strategy.d.ts +3 -0
  335. package/dist/withdraw-accessible-4JTDFRFZ.js +28 -0
  336. package/package.json +13 -5
  337. package/dist/api-WVY3H3YH.js +0 -20
  338. package/dist/backup-YYBE5RPC.js.map +0 -1
  339. package/dist/bundle-handle-JEUWU2SJ.js +0 -36
  340. package/dist/bundle-handle-JEUWU2SJ.js.map +0 -1
  341. package/dist/chunk-26JMGSSC.js.map +0 -1
  342. package/dist/chunk-3737A6EA.js.map +0 -1
  343. package/dist/chunk-4MC5JRRX.js.map +0 -1
  344. package/dist/chunk-52QYGZHH.js.map +0 -1
  345. package/dist/chunk-55LKVALY.js.map +0 -1
  346. package/dist/chunk-5PR33AC4.js.map +0 -1
  347. package/dist/chunk-66W5OOCX.js.map +0 -1
  348. package/dist/chunk-7G6VDWXB.js.map +0 -1
  349. package/dist/chunk-A67356IR.js.map +0 -1
  350. package/dist/chunk-AFHQATY4.js +0 -47
  351. package/dist/chunk-AFHQATY4.js.map +0 -1
  352. package/dist/chunk-AHLJSVRL.js.map +0 -1
  353. package/dist/chunk-C2OOEM57.js.map +0 -1
  354. package/dist/chunk-DIMKRG2E.js +0 -131
  355. package/dist/chunk-DIMKRG2E.js.map +0 -1
  356. package/dist/chunk-E6427JU2.js.map +0 -1
  357. package/dist/chunk-E7OBCBXS.js.map +0 -1
  358. package/dist/chunk-EDXAQ6JI.js.map +0 -1
  359. package/dist/chunk-EP7VPR7Y.js.map +0 -1
  360. package/dist/chunk-ERB6NDDW.js.map +0 -1
  361. package/dist/chunk-G5APARUV.js.map +0 -1
  362. package/dist/chunk-GASWQ3XG.js.map +0 -1
  363. package/dist/chunk-GLKDWWNO.js.map +0 -1
  364. package/dist/chunk-HNGSV2YC.js.map +0 -1
  365. package/dist/chunk-I4F6PKUK.js.map +0 -1
  366. package/dist/chunk-I4ZAO7F7.js.map +0 -1
  367. package/dist/chunk-I6SDX7WX.js.map +0 -1
  368. package/dist/chunk-JXIDGFKS.js.map +0 -1
  369. package/dist/chunk-LWSJG3LI.js.map +0 -1
  370. package/dist/chunk-MWKDOHEF.js.map +0 -1
  371. package/dist/chunk-MZ5ZFO2D.js.map +0 -1
  372. package/dist/chunk-NIVEDYJZ.js.map +0 -1
  373. package/dist/chunk-NWCGDAV6.js +0 -45
  374. package/dist/chunk-NWCGDAV6.js.map +0 -1
  375. package/dist/chunk-PJNTR2NV.js.map +0 -1
  376. package/dist/chunk-PRDRGL73.js.map +0 -1
  377. package/dist/chunk-PY7CI6L5.js.map +0 -1
  378. package/dist/chunk-RKKORO4M.js.map +0 -1
  379. package/dist/chunk-TAGFGKHP.js.map +0 -1
  380. package/dist/chunk-TDJPS3T2.js.map +0 -1
  381. package/dist/chunk-TOAIYQJR.js.map +0 -1
  382. package/dist/chunk-UHXCLPGC.js.map +0 -1
  383. package/dist/chunk-UIBGXRDD.js.map +0 -1
  384. package/dist/chunk-V5KYPBUR.js.map +0 -1
  385. package/dist/chunk-VCETL77O.js.map +0 -1
  386. package/dist/chunk-VN45YLWM.js.map +0 -1
  387. package/dist/chunk-VOQW5LAM.js.map +0 -1
  388. package/dist/chunk-VRA4Q7DA.js.map +0 -1
  389. package/dist/chunk-VV4NSB7P.js.map +0 -1
  390. package/dist/chunk-W5W6KHN2.js.map +0 -1
  391. package/dist/chunk-WHJKIZS7.js.map +0 -1
  392. package/dist/chunk-WYZ5MYUA.js.map +0 -1
  393. package/dist/chunk-ZJADGNYF.js.map +0 -1
  394. package/dist/chunk-ZNACADFG.js.map +0 -1
  395. package/dist/collection-facade-QN5ULWYC.js +0 -45
  396. package/dist/delegation-SSEPLXXO.js.map +0 -1
  397. package/dist/derive-6V3PIZF7.js +0 -22
  398. package/dist/executor-CYJQLKKI.js +0 -9
  399. package/dist/executor-FHHLXO4Y.js +0 -28
  400. package/dist/executor-S4QIKEHW.js +0 -9
  401. package/dist/export-accessible-PE5AYKZR.js +0 -23
  402. package/dist/extract-partition-X2L26O6A.js +0 -38
  403. package/dist/fanout-sidecar-DTBIDOWF.js.map +0 -1
  404. package/dist/find-RBBYKXI7.js +0 -11
  405. package/dist/issue-RNU55YOF.js +0 -19
  406. package/dist/liberate-PX4CAT5V.js +0 -24
  407. package/dist/noydb-YPSEGK32.js +0 -83
  408. package/dist/register-TMD6YRCX.js +0 -32
  409. package/dist/registry-CZVINVQH.js +0 -9
  410. package/dist/registry-FVLDWQKT.js +0 -18
  411. package/dist/registry-IKUR2MXD.js +0 -9
  412. package/dist/registry-JET4HBLY.js +0 -19
  413. package/dist/request-withdrawal-RH55LNZL.js +0 -31
  414. package/dist/revoke-R5TBYVA6.js +0 -24
  415. package/dist/seed-UK4QRIUN.js.map +0 -1
  416. package/dist/signer-AGALOXOO.js +0 -25
  417. package/dist/storage-N7LB275C.js +0 -23
  418. package/dist/store-coordination-provider-PB6QZ57W.js.map +0 -1
  419. package/dist/walk-OCUHCYJ6.js +0 -21
  420. package/dist/with-pod/bundle-handle.d.ts +0 -9
  421. package/dist/withdraw-accessible-QQNWM5BO.js +0 -28
  422. /package/dist/{api-WVY3H3YH.js.map → api-EYNT6KGS.js.map} +0 -0
  423. /package/dist/{chunk-35EBKWQU.js.map → chunk-2LFOMUEA.js.map} +0 -0
  424. /package/dist/{chunk-OF3L2SD6.js.map → chunk-32B7VCKU.js.map} +0 -0
  425. /package/dist/{chunk-MSWYQLMQ.js.map → chunk-3B6S6RSI.js.map} +0 -0
  426. /package/dist/{chunk-7F4DBNSP.js.map → chunk-3O72ZD2E.js.map} +0 -0
  427. /package/dist/{chunk-FNSMZQS4.js.map → chunk-3PMFMUFX.js.map} +0 -0
  428. /package/dist/{chunk-FAXZQ5UF.js.map → chunk-4RAS6HN6.js.map} +0 -0
  429. /package/dist/{chunk-VMCCGUQB.js.map → chunk-6JPGVL7X.js.map} +0 -0
  430. /package/dist/{chunk-WLYIROCJ.js.map → chunk-6YD5SYQS.js.map} +0 -0
  431. /package/dist/{chunk-YAWK6AWF.js.map → chunk-7KAE7CQ2.js.map} +0 -0
  432. /package/dist/{chunk-NKH7GSIH.js.map → chunk-AUPSEJ7O.js.map} +0 -0
  433. /package/dist/{chunk-CCZJJ4I2.js.map → chunk-B6IYATME.js.map} +0 -0
  434. /package/dist/{chunk-4UVDI4XE.js.map → chunk-CLAHBPOI.js.map} +0 -0
  435. /package/dist/{chunk-HVQGCKY4.js.map → chunk-D3R7Q4QK.js.map} +0 -0
  436. /package/dist/{chunk-P7OG2ETF.js.map → chunk-DXKLLKME.js.map} +0 -0
  437. /package/dist/{chunk-JIUVJ4M7.js.map → chunk-EKEIEJL6.js.map} +0 -0
  438. /package/dist/{chunk-DG27IUVI.js.map → chunk-FS7NTGCJ.js.map} +0 -0
  439. /package/dist/{chunk-HXGUIPQB.js.map → chunk-GQXFOAYZ.js.map} +0 -0
  440. /package/dist/{chunk-BTVYJWSN.js.map → chunk-GTHVPVAR.js.map} +0 -0
  441. /package/dist/{chunk-DMQCLZ4Z.js.map → chunk-GWO2IDAB.js.map} +0 -0
  442. /package/dist/{chunk-6HFM7EIT.js.map → chunk-H3G4I3PN.js.map} +0 -0
  443. /package/dist/{chunk-VJFMXYKP.js.map → chunk-HR5R5JOM.js.map} +0 -0
  444. /package/dist/{chunk-FDX7OWWI.js.map → chunk-HYYVT2KZ.js.map} +0 -0
  445. /package/dist/{chunk-JRCYZEHU.js.map → chunk-I6QKB7MT.js.map} +0 -0
  446. /package/dist/{chunk-KUDZVJKQ.js.map → chunk-IJ46VCWD.js.map} +0 -0
  447. /package/dist/{chunk-4HDYQL3J.js.map → chunk-INMHEMXX.js.map} +0 -0
  448. /package/dist/{chunk-AR2JDFAE.js.map → chunk-KV6JM2IA.js.map} +0 -0
  449. /package/dist/{chunk-PVU7LW5L.js.map → chunk-KX57HODD.js.map} +0 -0
  450. /package/dist/{chunk-CEJWMBXO.js.map → chunk-LXECF2ZR.js.map} +0 -0
  451. /package/dist/{chunk-7VHY5E3X.js.map → chunk-MSLPDFK2.js.map} +0 -0
  452. /package/dist/{chunk-HNHCQ556.js.map → chunk-NR2SUJ3H.js.map} +0 -0
  453. /package/dist/{chunk-W3TVWF6N.js.map → chunk-OIBEJN5H.js.map} +0 -0
  454. /package/dist/{chunk-QD724WDJ.js.map → chunk-OMYCSTXL.js.map} +0 -0
  455. /package/dist/{chunk-Z45YRGFO.js.map → chunk-ONVD6GBY.js.map} +0 -0
  456. /package/dist/{chunk-KCQX65FU.js.map → chunk-RKCRPPNB.js.map} +0 -0
  457. /package/dist/{chunk-7TSYBD3E.js.map → chunk-RSXQNFGO.js.map} +0 -0
  458. /package/dist/{chunk-7NXHMW6Y.js.map → chunk-SYS46BL7.js.map} +0 -0
  459. /package/dist/{chunk-747LRSOO.js.map → chunk-TMBSPLUG.js.map} +0 -0
  460. /package/dist/{chunk-WKPJXWKR.js.map → chunk-TX5MKAHA.js.map} +0 -0
  461. /package/dist/{chunk-NJDQ4YSV.js.map → chunk-YKXZHUOW.js.map} +0 -0
  462. /package/dist/{chunk-VW6ZZGCG.js.map → chunk-ZLHHE5XZ.js.map} +0 -0
  463. /package/dist/{chunk-FUHKW252.js.map → chunk-ZZXLB6AS.js.map} +0 -0
  464. /package/dist/{classified-marker-FTOX7Q5L.js.map → classified-marker-NA6HUV33.js.map} +0 -0
  465. /package/dist/{collection-facade-QN5ULWYC.js.map → collection-facade-VBM35WAH.js.map} +0 -0
  466. /package/dist/{computed-6G5M3UPH.js.map → computed-GSTLWSY5.js.map} +0 -0
  467. /package/dist/{dead-filter-ENRIOQIE.js.map → dead-filter-UALP5DJX.js.map} +0 -0
  468. /package/dist/{derive-6V3PIZF7.js.map → derive-YUO3TQYN.js.map} +0 -0
  469. /package/dist/{dispatch-RE74SH6L.js.map → dispatch-4ZHLSFZQ.js.map} +0 -0
  470. /package/dist/{dispatch-BDVGYC6X.js.map → dispatch-77CB75KB.js.map} +0 -0
  471. /package/dist/{enclave-52VUXNRL.js.map → enclave-O4NOROCJ.js.map} +0 -0
  472. /package/dist/{executor-CYJQLKKI.js.map → executor-CZDXBXKE.js.map} +0 -0
  473. /package/dist/{executor-FHHLXO4Y.js.map → executor-DVJAWN3B.js.map} +0 -0
  474. /package/dist/{executor-S4QIKEHW.js.map → executor-VRLG4TQJ.js.map} +0 -0
  475. /package/dist/{export-accessible-PE5AYKZR.js.map → export-accessible-UXSMG7AB.js.map} +0 -0
  476. /package/dist/{extract-partition-X2L26O6A.js.map → extract-partition-TVY7AMKH.js.map} +0 -0
  477. /package/dist/{find-RBBYKXI7.js.map → find-WSARMJBW.js.map} +0 -0
  478. /package/dist/{issue-RNU55YOF.js.map → issue-3JNJNPT3.js.map} +0 -0
  479. /package/dist/{ledger-4WJL66BW.js.map → ledger-MVPNG5OS.js.map} +0 -0
  480. /package/dist/{liberate-PX4CAT5V.js.map → liberate-4LOJOLDT.js.map} +0 -0
  481. /package/dist/{link-set-N4ZPV6E6.js.map → link-set-3POMQR3I.js.map} +0 -0
  482. /package/dist/{managed-secret-OTHS64IF.js.map → managed-secret-IARIJJ7Q.js.map} +0 -0
  483. /package/dist/{noydb-YPSEGK32.js.map → noydb-IRILUDED.js.map} +0 -0
  484. /package/dist/{post-register-ZAG2CAIY.js.map → post-register-Z5T7FY45.js.map} +0 -0
  485. /package/dist/{register-TMD6YRCX.js.map → register-UZ37QPDB.js.map} +0 -0
  486. /package/dist/{registry-CZVINVQH.js.map → registry-ETJNA7PO.js.map} +0 -0
  487. /package/dist/{registry-FVLDWQKT.js.map → registry-F7U6KSLX.js.map} +0 -0
  488. /package/dist/{registry-IKUR2MXD.js.map → registry-L4ELKX6C.js.map} +0 -0
  489. /package/dist/{registry-JET4HBLY.js.map → registry-SDT5LIGO.js.map} +0 -0
  490. /package/dist/{request-withdrawal-RH55LNZL.js.map → request-withdrawal-PVFEYX57.js.map} +0 -0
  491. /package/dist/{reveal-CMOJY4VO.js.map → reveal-DQ2E3QAZ.js.map} +0 -0
  492. /package/dist/{revoke-R5TBYVA6.js.map → revoke-YXY6SHUH.js.map} +0 -0
  493. /package/dist/{signer-AGALOXOO.js.map → signer-Q7XQBMPA.js.map} +0 -0
  494. /package/dist/{stale-ZWXSOWJI.js.map → stale-NI7SAXAZ.js.map} +0 -0
  495. /package/dist/{storage-N7LB275C.js.map → storage-3VNGSHXS.js.map} +0 -0
  496. /package/dist/{ulid-PTAIMDG4.js.map → ulid-VQW6ERQN.js.map} +0 -0
  497. /package/dist/{verify-RO5RTZOJ.js.map → verify-6J2YLKSO.js.map} +0 -0
  498. /package/dist/{walk-OCUHCYJ6.js.map → walk-WRA7RAPY.js.map} +0 -0
  499. /package/dist/{withdraw-accessible-QQNWM5BO.js.map → withdraw-accessible-4JTDFRFZ.js.map} +0 -0
@@ -5,7 +5,14 @@ import {
5
5
  readDirectoryConfig,
6
6
  readUserVisibility,
7
7
  saveUserEnvelope
8
- } from "./chunk-UIBGXRDD.js";
8
+ } from "./chunk-LX46B56Q.js";
9
+ import {
10
+ rekeyBlobSet,
11
+ rekeyEnvelopeIfNeeded
12
+ } from "./chunk-OHOKBMTM.js";
13
+ import {
14
+ buildRecordEnvelope
15
+ } from "./chunk-PENPHA45.js";
9
16
  import {
10
17
  base64ToBuffer,
11
18
  bufferToBase64,
@@ -20,25 +27,73 @@ import {
20
27
  generateSalt,
21
28
  unwrapKey,
22
29
  wrapKey
23
- } from "./chunk-VCETL77O.js";
30
+ } from "./chunk-AU3AUJIZ.js";
24
31
  import {
25
- NOYDB_FORMAT_VERSION,
26
32
  NOYDB_KEYRING_VERSION
27
- } from "./chunk-W5W6KHN2.js";
33
+ } from "./chunk-IGNJOJEP.js";
28
34
  import {
29
35
  DirectoryDisabledError,
30
36
  EchoCeremonyRequiredError,
31
37
  InvalidKeyError,
32
38
  KeyringCorruptError,
33
39
  KeyringExpiredError,
40
+ KeyringTamperedError,
34
41
  NoAccessError,
35
42
  NoydbError,
36
43
  PermissionDeniedError,
37
44
  PrivilegeEscalationError,
45
+ ROSTER_KEY_ID,
38
46
  USER_ENVELOPE_COLLECTION,
39
47
  ValidationError,
40
48
  WrongPromptError
41
- } from "./chunk-HNGSV2YC.js";
49
+ } from "./chunk-CWM4GQD2.js";
50
+
51
+ // src/with-party/team/roster-tag.ts
52
+ function stable(value) {
53
+ if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
54
+ if (value !== null && typeof value === "object") {
55
+ 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)}`);
56
+ return `{${entries.join(",")}}`;
57
+ }
58
+ return JSON.stringify(value) ?? "null";
59
+ }
60
+ function rosterCanonical(file) {
61
+ return stable({
62
+ user_id: file.user_id,
63
+ role: file.role,
64
+ permissions: file.permissions,
65
+ granted_by: file.granted_by,
66
+ expires_at: file.expires_at ?? null,
67
+ export_capability: file.export_capability ?? null,
68
+ import_capability: file.import_capability ?? null
69
+ });
70
+ }
71
+ async function mintRosterTag(file, rosterKey) {
72
+ return encrypt(rosterCanonical(file), rosterKey);
73
+ }
74
+ async function verifyRosterTag(file, tag, rosterKey) {
75
+ if (tag == null) return false;
76
+ try {
77
+ return await decrypt(tag.iv, tag.data, rosterKey) === rosterCanonical(file);
78
+ } catch {
79
+ return false;
80
+ }
81
+ }
82
+ async function assertRosterAuthenticated(file, deks, userId) {
83
+ const rosterKey = deks.get(ROSTER_KEY_ID);
84
+ if (rosterKey === void 0) {
85
+ throw new KeyringTamperedError({ userId, reason: "roster-key-missing" });
86
+ }
87
+ await assertRosterTagValid(file, rosterKey, userId);
88
+ }
89
+ async function assertRosterTagValid(file, rosterKey, userId) {
90
+ if (!await verifyRosterTag(file, file.roster_tag, rosterKey)) {
91
+ throw new KeyringTamperedError({
92
+ userId,
93
+ reason: file.roster_tag == null ? "roster-tag-missing" : "roster-tag-mismatch"
94
+ });
95
+ }
96
+ }
42
97
 
43
98
  // src/kernel/validation.ts
44
99
  var WeakSecretError = class extends NoydbError {
@@ -294,6 +349,18 @@ async function verifyKeyringCanary(wrappedCanary, kek) {
294
349
  return false;
295
350
  }
296
351
  }
352
+ function rosterKeyOf(keyring) {
353
+ return keyring.deks.get(ROSTER_KEY_ID) ?? null;
354
+ }
355
+ function requireRosterKey(keyring, fn) {
356
+ const rosterKey = rosterKeyOf(keyring);
357
+ if (!rosterKey) {
358
+ throw new ValidationError(
359
+ `${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.`
360
+ );
361
+ }
362
+ return rosterKey;
363
+ }
297
364
  async function deriveKekForKeyring(file, secret, salt) {
298
365
  if (file.echo !== void 0) {
299
366
  if (typeof secret === "string") throw new EchoCeremonyRequiredError();
@@ -327,10 +394,12 @@ async function loadKeyring(store, vault, opts) {
327
394
  throw new NoAccessError(`No keyring found for user "${userId}" in vault "${vault}"`);
328
395
  }
329
396
  const { file: keyringFile } = found;
330
- assertKeyringNotExpired(keyringFile);
331
397
  const salt = base64ToBuffer(keyringFile.salt);
332
398
  const kek = await deriveKekForKeyring(keyringFile, secret, salt);
333
- const canaryOk = keyringFile.canary !== void 0 ? await verifyKeyringCanary(keyringFile.canary, kek) : null;
399
+ if (keyringFile.canary === void 0) {
400
+ throw new KeyringTamperedError({ userId, reason: "canary-missing" });
401
+ }
402
+ const canaryOk = await verifyKeyringCanary(keyringFile.canary, kek);
334
403
  const deks = /* @__PURE__ */ new Map();
335
404
  const failedCollections = [];
336
405
  let firstUnwrapError = null;
@@ -343,11 +412,18 @@ async function loadKeyring(store, vault, opts) {
343
412
  if (firstUnwrapError === null) firstUnwrapError = err;
344
413
  }
345
414
  }
346
- if (canaryOk === true) {
415
+ const pendingDeks = /* @__PURE__ */ new Map();
416
+ for (const [collName, wrapped] of Object.entries(keyringFile.pending_deks ?? {})) {
417
+ try {
418
+ pendingDeks.set(collName, await unwrapKey(wrapped, kek));
419
+ } catch {
420
+ }
421
+ }
422
+ if (canaryOk) {
347
423
  if (failedCollections.length > 0) {
348
424
  throw new KeyringCorruptError({ failedCollections, intactCount: deks.size });
349
425
  }
350
- } else if (canaryOk === false) {
426
+ } else {
351
427
  if (deks.size > 0) {
352
428
  throw new KeyringCorruptError({
353
429
  failedCollections: [...failedCollections, "_canary"],
@@ -355,20 +431,16 @@ async function loadKeyring(store, vault, opts) {
355
431
  });
356
432
  }
357
433
  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
434
  }
435
+ await assertRosterAuthenticated(keyringFile, deks, userId);
436
+ assertKeyringNotExpired(keyringFile);
366
437
  return {
367
438
  userId: keyringFile.user_id,
368
439
  displayName: keyringFile.display_name,
369
440
  role: keyringFile.role,
370
441
  permissions: keyringFile.permissions,
371
442
  deks,
443
+ pendingDeks,
372
444
  kek,
373
445
  salt,
374
446
  authenticators: keyringFile.authenticators ?? [],
@@ -404,18 +476,27 @@ async function createOwnerKeyring(store, vault, opts) {
404
476
  const kek = typeof secret === "string" ? await deriveKey(secret, salt) : await deriveEchoKey(secret, salt);
405
477
  const userEnvelopeDek = await generateDEK();
406
478
  const wrappedUserEnvelopeDek = await wrapKey(userEnvelopeDek, kek);
479
+ const rosterKey = await generateDEK();
480
+ const wrappedRosterKey = await wrapKey(rosterKey, kek);
407
481
  const canary = await mintKeyringCanary(kek);
408
- const keyringFile = {
409
- _noydb_keyring: NOYDB_KEYRING_VERSION,
482
+ const authority = {
410
483
  user_id: userId,
411
484
  display_name: userId,
412
485
  role: "owner",
413
486
  permissions: {},
414
- deks: { [USER_ENVELOPE_COLLECTION]: wrappedUserEnvelopeDek },
487
+ granted_by: userId
488
+ };
489
+ const keyringFile = {
490
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
491
+ ...authority,
492
+ deks: {
493
+ [USER_ENVELOPE_COLLECTION]: wrappedUserEnvelopeDek,
494
+ [ROSTER_KEY_ID]: wrappedRosterKey
495
+ },
415
496
  salt: bufferToBase64(salt),
416
497
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
417
- granted_by: userId,
418
498
  canary,
499
+ roster_tag: await mintRosterTag(authority, rosterKey),
419
500
  // The presence of this block is what makes the keyring an echo keyring —
420
501
  // `deriveKekForKeyring` reads it to refuse a single-string unlock (AG-1).
421
502
  ...typeof secret !== "string" ? {
@@ -432,7 +513,10 @@ async function createOwnerKeyring(store, vault, opts) {
432
513
  displayName: userId,
433
514
  role: "owner",
434
515
  permissions: {},
435
- deks: /* @__PURE__ */ new Map([[USER_ENVELOPE_COLLECTION, userEnvelopeDek]]),
516
+ deks: /* @__PURE__ */ new Map([
517
+ [USER_ENVELOPE_COLLECTION, userEnvelopeDek],
518
+ [ROSTER_KEY_ID, rosterKey]
519
+ ]),
436
520
  kek,
437
521
  salt,
438
522
  authenticators: []
@@ -444,11 +528,18 @@ async function grant(store, vault, callerKeyring, options) {
444
528
  "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
529
  );
446
530
  }
531
+ const callerRosterKey = requireRosterKey(callerKeyring, "grant");
447
532
  if (!canGrant(callerKeyring.role, options.role)) {
448
533
  throw new PermissionDeniedError(
449
534
  `Role "${callerKeyring.role}" cannot grant role "${options.role}"`
450
535
  );
451
536
  }
537
+ const secret = options.secret;
538
+ if (typeof secret !== "string" || secret.trim() === "") {
539
+ throw new ValidationError(
540
+ `grant: \`secret\` is required and must be a non-empty string (got ${secret === void 0 ? "undefined" : typeof secret === "string" ? "an empty string" : typeof secret}). The grantee's key is derived from it, so a missing secret produces a keyring slot nobody can unlock. Note the 0.4.0-pre rename: the option is \`secret\`, not \`passphrase\`.`
541
+ );
542
+ }
452
543
  if (options.validateSecret && !options.allowWeakSecret) {
453
544
  assertStrongSecret(options.secret);
454
545
  }
@@ -456,15 +547,26 @@ async function grant(store, vault, callerKeyring, options) {
456
547
  const newSalt = generateSalt();
457
548
  const newKek = await deriveKey(options.secret, newSalt);
458
549
  const granteeMayHoldSecrets = options.role === "owner" || options.role === "admin";
550
+ let mintedForGrant = false;
551
+ for (const collName of Object.keys(permissions)) {
552
+ if (collName === PERMISSION_WILDCARD) continue;
553
+ if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
554
+ if (callerKeyring.deks.has(collName)) continue;
555
+ if (await collectionHasRecords(store, vault, collName)) continue;
556
+ callerKeyring.deks.set(collName, await generateDEK());
557
+ mintedForGrant = true;
558
+ }
559
+ if (mintedForGrant) await persistKeyring(store, vault, callerKeyring);
459
560
  const wrappedDeks = {};
460
561
  for (const collName of Object.keys(permissions)) {
562
+ if (collName === PERMISSION_WILDCARD) continue;
461
563
  if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
462
564
  const dek = callerKeyring.deks.get(collName);
463
565
  if (dek) {
464
566
  wrappedDeks[collName] = await wrapKey(dek, newKek);
465
567
  }
466
568
  }
467
- if (options.role === "owner" || options.role === "admin" || options.role === "custodian" || options.role === "viewer") {
569
+ if (options.role === "owner" || options.role === "admin" || options.role === "custodian" || options.role === "viewer" || permissionsAreWildcard(options.permissions)) {
468
570
  for (const [collName, dek] of callerKeyring.deks) {
469
571
  if (collName in wrappedDeks) continue;
470
572
  if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
@@ -482,19 +584,25 @@ async function grant(store, vault, callerKeyring, options) {
482
584
  }
483
585
  }
484
586
  const canary = await mintKeyringCanary(newKek);
485
- const keyringFile = {
486
- _noydb_keyring: NOYDB_KEYRING_VERSION,
587
+ const authority = {
487
588
  user_id: options.userId,
488
589
  display_name: options.displayName,
489
590
  role: options.role,
490
591
  permissions,
592
+ granted_by: callerKeyring.userId,
593
+ ...options.exportCapability !== void 0 && { export_capability: options.exportCapability },
594
+ ...options.importCapability !== void 0 && { import_capability: options.importCapability }
595
+ };
596
+ const keyringFile = {
597
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
598
+ ...authority,
491
599
  deks: wrappedDeks,
492
600
  salt: bufferToBase64(newSalt),
493
601
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
494
- granted_by: callerKeyring.userId,
495
602
  canary,
496
- ...options.exportCapability !== void 0 && { export_capability: options.exportCapability },
497
- ...options.importCapability !== void 0 && { import_capability: options.importCapability }
603
+ // The grantee's own copy of the roster key rode in via the `_`-prefix
604
+ // propagation loop above, so they can verify this tag on first unlock.
605
+ roster_tag: await mintRosterTag(authority, callerRosterKey)
498
606
  };
499
607
  await writeKeyringFile(store, vault, options.userId, keyringFile);
500
608
  const userEnvelopeDek = callerKeyring.deks.get(USER_ENVELOPE_COLLECTION);
@@ -509,13 +617,14 @@ async function grant(store, vault, callerKeyring, options) {
509
617
  );
510
618
  }
511
619
  }
512
- async function findAdminDescendants(store, vault, rootUserId) {
620
+ async function findAdminDescendants(store, vault, rootUserId, rosterKey) {
513
621
  const allUserIds = await store.list(vault, "_keyring");
514
622
  const childrenByParent = /* @__PURE__ */ new Map();
515
623
  for (const userId of allUserIds) {
516
624
  const found = await readKeyringFile(store, vault, userId);
517
625
  if (!found) continue;
518
626
  const kf = found.file;
627
+ await assertRosterTagValid(kf, rosterKey, userId);
519
628
  if (kf.role !== "admin") continue;
520
629
  if (kf.user_id === rootUserId) continue;
521
630
  const list = childrenByParent.get(kf.granted_by) ?? [];
@@ -539,9 +648,16 @@ async function findAdminDescendants(store, vault, rootUserId) {
539
648
  async function revoke(store, vault, callerKeyring, options) {
540
649
  const targetFound = await readKeyringFile(store, vault, options.userId);
541
650
  if (!targetFound) {
651
+ const pending = [...callerKeyring.pendingDeks?.keys() ?? []];
652
+ if (pending.length > 0) {
653
+ await rotateKeys(store, vault, callerKeyring, { collections: pending });
654
+ return;
655
+ }
542
656
  throw new NoAccessError(`User "${options.userId}" has no keyring in vault "${vault}"`);
543
657
  }
544
658
  const targetKeyring = targetFound.file;
659
+ const callerRosterKey = requireRosterKey(callerKeyring, "revoke");
660
+ await assertRosterTagValid(targetKeyring, callerRosterKey, options.userId);
545
661
  if (!canRevoke(callerKeyring.role, targetKeyring.role)) {
546
662
  throw new PermissionDeniedError(
547
663
  `Role "${callerKeyring.role}" cannot revoke role "${targetKeyring.role}"`
@@ -551,7 +667,7 @@ async function revoke(store, vault, callerKeyring, options) {
551
667
  const usersToRevoke = [options.userId];
552
668
  const affectedCollections = new Set(Object.keys(targetKeyring.deks));
553
669
  if (targetKeyring.role === "admin") {
554
- const descendants = await findAdminDescendants(store, vault, options.userId);
670
+ const descendants = await findAdminDescendants(store, vault, options.userId, callerRosterKey);
555
671
  if (descendants.length > 0) {
556
672
  if (cascadeMode === "warn") {
557
673
  console.warn(
@@ -561,6 +677,7 @@ async function revoke(store, vault, callerKeyring, options) {
561
677
  for (const userId of descendants) {
562
678
  const descFound = await readKeyringFile(store, vault, userId);
563
679
  if (!descFound) continue;
680
+ await assertRosterTagValid(descFound.file, callerRosterKey, userId);
564
681
  usersToRevoke.push(userId);
565
682
  for (const c of Object.keys(descFound.file.deks)) affectedCollections.add(c);
566
683
  }
@@ -572,9 +689,103 @@ async function revoke(store, vault, callerKeyring, options) {
572
689
  await deleteUserEnvelope(store, vault, userId);
573
690
  await deleteUserVisibility(store, vault, userId);
574
691
  }
575
- if (options.rotateKeys !== false && affectedCollections.size > 0) {
576
- await rotateKeys(store, vault, callerKeyring, { collections: [...affectedCollections] });
692
+ affectedCollections.delete(ROSTER_KEY_ID);
693
+ if (affectedCollections.size > 0) {
694
+ const { unverified } = await rotateKeys(store, vault, callerKeyring, {
695
+ collections: [...affectedCollections]
696
+ });
697
+ if (unverified.length > 0) {
698
+ console.warn(
699
+ `[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).`
700
+ );
701
+ }
702
+ }
703
+ }
704
+ async function readKeyringForAudit(store, vault, userId) {
705
+ const envelope = await store.get(vault, "_keyring", userId);
706
+ if (!envelope) return void 0;
707
+ try {
708
+ return { file: parseKeyringEnvelope(envelope) };
709
+ } catch {
710
+ return { unparseable: true };
711
+ }
712
+ }
713
+ async function verifyRoster(store, vault, callerKeyring) {
714
+ const rosterKey = requireRosterKey(callerKeyring, "verifyRoster");
715
+ const unverified = [];
716
+ let checked = 0;
717
+ for (const userId of await store.list(vault, "_keyring")) {
718
+ const found = await readKeyringForAudit(store, vault, userId);
719
+ if (!found) continue;
720
+ checked += 1;
721
+ if ("unparseable" in found) {
722
+ unverified.push({ userId, reason: "unparseable" });
723
+ continue;
724
+ }
725
+ try {
726
+ await assertRosterTagValid(found.file, rosterKey, userId);
727
+ } catch (err) {
728
+ if (!(err instanceof KeyringTamperedError)) throw err;
729
+ unverified.push({ userId, reason: err.details.reason });
730
+ }
731
+ }
732
+ return { checked, unverified };
733
+ }
734
+ async function quarantineKeyring(store, vault, callerKeyring, userId) {
735
+ if (callerKeyring.role !== "owner") {
736
+ throw new PermissionDeniedError(
737
+ `Role "${callerKeyring.role}" cannot quarantine a keyring \u2014 quarantine is owner-only, because it removes a file whose own authority claims cannot be trusted.`
738
+ );
739
+ }
740
+ if (userId === callerKeyring.userId) {
741
+ throw new ValidationError(
742
+ "quarantineKeyring: refusing to quarantine the calling keyring. Removing your own keyring and rotating behind it would leave the vault with no reachable owner."
743
+ );
744
+ }
745
+ const rosterKey = requireRosterKey(callerKeyring, "quarantineKeyring");
746
+ const found = await readKeyringForAudit(store, vault, userId);
747
+ if (!found) {
748
+ const pending = [...callerKeyring.pendingDeks?.keys() ?? []];
749
+ if (pending.length > 0) {
750
+ const resumed = await rotateKeys(store, vault, callerKeyring, { collections: pending });
751
+ return {
752
+ userId,
753
+ reason: "roster-tag-mismatch",
754
+ rotated: pending,
755
+ needsRegrant: resumed.needsRegrant,
756
+ alsoUnverified: resumed.unverified
757
+ };
758
+ }
759
+ throw new NoAccessError(`No keyring found for user "${userId}" in vault "${vault}"`);
577
760
  }
761
+ let reason = null;
762
+ if ("unparseable" in found) {
763
+ reason = "unparseable";
764
+ } else {
765
+ try {
766
+ await assertRosterTagValid(found.file, rosterKey, userId);
767
+ } catch (err) {
768
+ if (!(err instanceof KeyringTamperedError)) throw err;
769
+ reason = err.details.reason;
770
+ }
771
+ }
772
+ if (reason === null) {
773
+ throw new ValidationError(
774
+ `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.`
775
+ );
776
+ }
777
+ await store.delete(vault, "_keyring", userId);
778
+ await deleteUserEnvelope(store, vault, userId);
779
+ await deleteUserVisibility(store, vault, userId);
780
+ const rotated = [...callerKeyring.deks.keys()].filter((c) => c !== ROSTER_KEY_ID);
781
+ const result = rotated.length > 0 ? await rotateKeys(store, vault, callerKeyring, { collections: rotated }) : { needsRegrant: [], unverified: [] };
782
+ return {
783
+ userId,
784
+ reason,
785
+ rotated,
786
+ needsRegrant: result.needsRegrant,
787
+ alsoUnverified: result.unverified
788
+ };
578
789
  }
579
790
  async function updateKeyringIdentity(store, vault, callerKeyring, options) {
580
791
  if (options.role === void 0 && options.displayName === void 0 && options.permissions === void 0) {
@@ -589,6 +800,8 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
589
800
  );
590
801
  }
591
802
  const target = found.file;
803
+ const rosterKey = requireRosterKey(callerKeyring, "updateKeyringIdentity");
804
+ await assertRosterTagValid(target, rosterKey, options.userId);
592
805
  if (!canUpdateRole(callerKeyring.role, target.role)) {
593
806
  throw new PermissionDeniedError(
594
807
  `Role "${callerKeyring.role}" cannot update a keyring with role "${target.role}"`
@@ -599,7 +812,7 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
599
812
  `Role "${callerKeyring.role}" cannot promote target to role "${options.role}"`
600
813
  );
601
814
  }
602
- const next = {
815
+ const edited = {
603
816
  ...target,
604
817
  ...options.role !== void 0 && { role: options.role },
605
818
  ...options.displayName !== void 0 && {
@@ -608,50 +821,113 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
608
821
  },
609
822
  ...options.permissions !== void 0 && { permissions: options.permissions }
610
823
  };
824
+ const next = { ...edited, roster_tag: await mintRosterTag(edited, rosterKey) };
611
825
  await writeKeyringFile(store, vault, options.userId, next);
612
826
  }
827
+ var HISTORY_COLLECTION = "_history";
828
+ var LEDGER_COLLECTION = "_ledger";
829
+ var LEDGER_DELTAS_COLLECTION = "_ledger_deltas";
830
+ var BLOB_COLLECTION = "_blob";
831
+ var BLOB_SLOTS_PREFIX = "_blob_slots_";
832
+ var BLOB_VERSIONS_PREFIX = "_blob_versions_";
833
+ var BLOB_INTENT_COLLECTION = "_blob_intent";
834
+ async function listOrEmpty(store, vault, collection) {
835
+ try {
836
+ return await store.list(vault, collection);
837
+ } catch {
838
+ return [];
839
+ }
840
+ }
841
+ async function derivedRefsFor(store, vault, rotated) {
842
+ const out = [];
843
+ if (!rotated.startsWith("_")) {
844
+ for (const id of await listOrEmpty(store, vault, HISTORY_COLLECTION)) {
845
+ if (id.startsWith(`${rotated}:`)) out.push({ collection: HISTORY_COLLECTION, id });
846
+ }
847
+ }
848
+ if (rotated === LEDGER_COLLECTION) {
849
+ for (const id of await listOrEmpty(store, vault, LEDGER_DELTAS_COLLECTION)) {
850
+ out.push({ collection: LEDGER_DELTAS_COLLECTION, id });
851
+ }
852
+ }
853
+ if (!rotated.startsWith("_")) {
854
+ for (const id of await listOrEmpty(store, vault, `${BLOB_SLOTS_PREFIX}${rotated}`)) {
855
+ out.push({ collection: `${BLOB_SLOTS_PREFIX}${rotated}`, id });
856
+ }
857
+ for (const id of await listOrEmpty(store, vault, `${BLOB_VERSIONS_PREFIX}${rotated}`)) {
858
+ out.push({ collection: `${BLOB_VERSIONS_PREFIX}${rotated}`, id });
859
+ }
860
+ for (const id of await listOrEmpty(store, vault, BLOB_INTENT_COLLECTION)) {
861
+ if (id.startsWith(`${rotated}::`)) out.push({ collection: BLOB_INTENT_COLLECTION, id });
862
+ }
863
+ }
864
+ return out;
865
+ }
613
866
  async function rotateKeys(store, vault, callerKeyring, opts) {
614
867
  const { collections } = opts;
868
+ if (collections.includes(ROSTER_KEY_ID)) {
869
+ throw new ValidationError(
870
+ `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\`.`
871
+ );
872
+ }
615
873
  if (callerKeyring.role === "custodian") {
616
874
  throw new PermissionDeniedError(
617
875
  "custodian cannot rotate keys (FR-6: re-key is an owner-only meta-capability; use the Deed owner)"
618
876
  );
619
877
  }
878
+ const callerRosterKey = requireRosterKey(callerKeyring, "rotateKeys");
620
879
  const newDeks = /* @__PURE__ */ new Map();
621
880
  for (const collName of collections) {
622
- newDeks.set(collName, await generateDEK());
881
+ newDeks.set(collName, callerKeyring.pendingDeks?.get(collName) ?? await generateDEK());
623
882
  }
883
+ const pending = callerKeyring.pendingDeks ?? /* @__PURE__ */ new Map();
884
+ callerKeyring.pendingDeks = pending;
885
+ for (const [collName, newDek] of newDeks) {
886
+ pending.set(collName, newDek);
887
+ }
888
+ await persistKeyring(store, vault, callerKeyring);
624
889
  for (const collName of collections) {
625
890
  const oldDek = callerKeyring.deks.get(collName);
626
891
  const newDek = newDeks.get(collName);
627
892
  if (!oldDek) continue;
628
- const ids = await store.list(vault, collName);
629
- for (const id of ids) {
630
- const envelope = await store.get(vault, collName, id);
893
+ if (collName === BLOB_COLLECTION || collName.startsWith(`${BLOB_COLLECTION}#`)) {
894
+ const others = [];
895
+ for (const [name, dek] of callerKeyring.deks) if (name !== collName) others.push(dek);
896
+ for (const [name, dek] of newDeks) if (name !== collName) others.push(dek);
897
+ await rekeyBlobSet(store, vault, oldDek, newDek, others);
898
+ }
899
+ const refs = [
900
+ ...(await store.list(vault, collName)).map((id) => ({ collection: collName, id })),
901
+ ...await derivedRefsFor(store, vault, collName)
902
+ ];
903
+ for (const ref of refs) {
904
+ const { collection: refColl, id } = ref;
905
+ const envelope = await store.get(vault, refColl, id);
631
906
  if (!envelope || !envelope._iv) continue;
632
- const plaintext = await decrypt(envelope._iv, envelope._data, oldDek);
633
- const { iv, data } = await encrypt(plaintext, newDek);
634
- const newEnvelope = {
635
- _noydb: NOYDB_FORMAT_VERSION,
636
- _v: envelope._v,
637
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
638
- _iv: iv,
639
- _data: data
640
- };
641
- await store.put(vault, collName, id, newEnvelope);
907
+ const newEnvelope = await rekeyEnvelopeIfNeeded({ collection: refColl, id }, envelope, oldDek, newDek);
908
+ if (newEnvelope !== null) await store.put(vault, refColl, id, newEnvelope);
642
909
  }
643
910
  }
644
911
  for (const [collName, newDek] of newDeks) {
645
912
  callerKeyring.deks.set(collName, newDek);
913
+ callerKeyring.pendingDeks?.delete(collName);
646
914
  }
647
915
  await persistKeyring(store, vault, callerKeyring);
648
916
  const needsRegrant = [];
917
+ const unverified = [];
649
918
  const userIds = await store.list(vault, "_keyring");
650
919
  for (const userId of userIds) {
651
920
  if (userId === callerKeyring.userId) continue;
652
921
  const userFound = await readKeyringFile(store, vault, userId);
653
922
  if (!userFound) continue;
654
923
  const userKeyringFile = userFound.file;
924
+ try {
925
+ await assertRosterTagValid(userKeyringFile, callerRosterKey, userId);
926
+ } catch (err) {
927
+ if (!(err instanceof KeyringTamperedError)) throw err;
928
+ unverified.push({ userId, reason: err.details.reason });
929
+ continue;
930
+ }
655
931
  const updatedDeks = { ...userKeyringFile.deks };
656
932
  const updatedPermissions = { ...userKeyringFile.permissions };
657
933
  for (const collName of collections) {
@@ -661,17 +937,24 @@ async function rotateKeys(store, vault, callerKeyring, opts) {
661
937
  delete updatedDeks[collName];
662
938
  delete updatedPermissions[collName];
663
939
  }
664
- const updatedKeyring = {
940
+ const edited = {
665
941
  ...userKeyringFile,
666
942
  deks: updatedDeks,
667
943
  permissions: updatedPermissions
668
944
  };
945
+ const updatedKeyring = {
946
+ ...edited,
947
+ roster_tag: await mintRosterTag(edited, callerRosterKey)
948
+ };
669
949
  await writeKeyringFile(store, vault, userId, updatedKeyring);
670
950
  }
671
- return { needsRegrant };
951
+ return { needsRegrant, unverified };
672
952
  }
673
953
  async function changeSecret(store, vault, keyring, opts) {
674
954
  const existingFound = await readKeyringFile(store, vault, keyring.userId);
955
+ if (existingFound) {
956
+ await assertRosterTagValid(existingFound.file, requireRosterKey(keyring, "changeSecret"), keyring.userId);
957
+ }
675
958
  if (existingFound && existingFound.file.echo !== void 0) throw new EchoCeremonyRequiredError();
676
959
  const { newSecret } = opts;
677
960
  if (!opts.allowWeakSecret) {
@@ -683,18 +966,28 @@ async function changeSecret(store, vault, keyring, opts) {
683
966
  for (const [collName, dek] of keyring.deks) {
684
967
  wrappedDeks[collName] = await wrapKey(dek, newKek);
685
968
  }
969
+ const rosterKey = requireRosterKey(keyring, "changeSecret");
686
970
  const canary = await mintKeyringCanary(newKek);
687
- const keyringFile = {
688
- _noydb_keyring: NOYDB_KEYRING_VERSION,
971
+ const existingAuthority = existingFound?.file;
972
+ const keptGrantedBy = existingAuthority?.granted_by ?? keyring.userId;
973
+ const authority = {
689
974
  user_id: keyring.userId,
690
975
  display_name: keyring.displayName,
691
976
  role: keyring.role,
692
977
  permissions: keyring.permissions,
978
+ granted_by: keptGrantedBy,
979
+ ...existingAuthority?.expires_at !== void 0 && { expires_at: existingAuthority.expires_at },
980
+ ...existingAuthority?.export_capability !== void 0 && { export_capability: existingAuthority.export_capability },
981
+ ...existingAuthority?.import_capability !== void 0 && { import_capability: existingAuthority.import_capability }
982
+ };
983
+ const keyringFile = {
984
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
985
+ ...authority,
693
986
  deks: wrappedDeks,
694
987
  salt: bufferToBase64(newSalt),
695
988
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
696
- granted_by: keyring.userId,
697
- canary
989
+ canary,
990
+ roster_tag: await mintRosterTag(authority, rosterKey)
698
991
  };
699
992
  await writeKeyringFile(store, vault, keyring.userId, keyringFile);
700
993
  return {
@@ -721,6 +1014,7 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
721
1014
  "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."
722
1015
  );
723
1016
  }
1017
+ const rosterKey = requireRosterKey(callerKeyring, "buildRecipientKeyringFile");
724
1018
  const role = recipient.role ?? "viewer";
725
1019
  const permissions = resolvePermissions(role, recipient.permissions);
726
1020
  const newSalt = generateSalt();
@@ -750,20 +1044,26 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
750
1044
  }
751
1045
  }
752
1046
  const canary = await mintKeyringCanary(newKek);
753
- return {
754
- _noydb_keyring: NOYDB_KEYRING_VERSION,
1047
+ const authority = {
755
1048
  user_id: recipient.id,
756
1049
  display_name: recipient.displayName ?? recipient.id,
757
1050
  role,
758
1051
  permissions,
1052
+ granted_by: callerKeyring.userId,
1053
+ ...recipient.exportCapability !== void 0 ? { export_capability: recipient.exportCapability } : {},
1054
+ ...recipient.importCapability !== void 0 ? { import_capability: recipient.importCapability } : {},
1055
+ ...recipient.expiresAt !== void 0 ? { expires_at: recipient.expiresAt } : {}
1056
+ };
1057
+ return {
1058
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
1059
+ ...authority,
759
1060
  deks: wrappedDeks,
760
1061
  salt: bufferToBase64(newSalt),
761
1062
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
762
- granted_by: callerKeyring.userId,
763
1063
  canary,
764
- ...recipient.exportCapability !== void 0 ? { export_capability: recipient.exportCapability } : {},
765
- ...recipient.importCapability !== void 0 ? { import_capability: recipient.importCapability } : {},
766
- ...recipient.expiresAt !== void 0 ? { expires_at: recipient.expiresAt } : {},
1064
+ // The recipient's copy of the roster key rode in via the `_`-prefix
1065
+ // propagation loop above, so the slot verifies on first unlock.
1066
+ roster_tag: await mintRosterTag(authority, rosterKey),
767
1067
  // The presence of this block is what makes the recipient's slot an
768
1068
  // echo keyring — mirrors `createOwnerKeyring`'s same dance so the
769
1069
  // ceremony (prompt → echo → key) travels with the pod, not just a
@@ -817,6 +1117,23 @@ async function listUsersWithEnvelopes(store, vault, userEnvelopeDek, callerRole,
817
1117
  }
818
1118
  return out;
819
1119
  }
1120
+ async function collectionHasRecords(store, vault, collectionName) {
1121
+ const ids = await store.list(vault, collectionName);
1122
+ return ids.length > 0;
1123
+ }
1124
+ async function reloadPersistedDEK(store, vault, keyring, collectionName) {
1125
+ if (!keyring.kek) return null;
1126
+ const found = await readKeyringFile(store, vault, keyring.userId);
1127
+ const wrapped = found?.file.deks[collectionName];
1128
+ if (wrapped === void 0) return null;
1129
+ try {
1130
+ const dek = await unwrapKey(wrapped, keyring.kek);
1131
+ keyring.deks.set(collectionName, dek);
1132
+ return dek;
1133
+ } catch {
1134
+ return null;
1135
+ }
1136
+ }
820
1137
  async function ensureCollectionDEK(store, vault, keyring) {
821
1138
  const inFlight = /* @__PURE__ */ new Map();
822
1139
  return async (collectionName) => {
@@ -825,6 +1142,20 @@ async function ensureCollectionDEK(store, vault, keyring) {
825
1142
  const pending = inFlight.get(collectionName);
826
1143
  if (pending) return pending;
827
1144
  const promise = (async () => {
1145
+ if (!collectionName.startsWith("_")) {
1146
+ if (!hasAccess(keyring, collectionName)) {
1147
+ throw new NoAccessError(
1148
+ `No access \u2014 user does not have a key for collection "${collectionName}". Grant them this collection in \`permissions\` to give them one.`
1149
+ );
1150
+ }
1151
+ if (await collectionHasRecords(store, vault, collectionName)) {
1152
+ const refreshed = await reloadPersistedDEK(store, vault, keyring, collectionName);
1153
+ if (refreshed) return refreshed;
1154
+ throw new NoAccessError(
1155
+ `No access \u2014 user "${keyring.userId}" is entitled to collection "${collectionName}" but holds no key for it, because the collection was created AFTER their grant. A collection DEK can only be wrapped at grant time (wrapping needs the grantee's secret, which the vault never stores), so this cannot be back-filled \u2014 re-grant the user to give them the key.`
1156
+ );
1157
+ }
1158
+ }
828
1159
  const dek = await generateDEK();
829
1160
  keyring.deks.set(collectionName, dek);
830
1161
  await persistKeyring(store, vault, keyring);
@@ -838,14 +1169,20 @@ async function ensureCollectionDEK(store, vault, keyring) {
838
1169
  }
839
1170
  };
840
1171
  }
1172
+ var PERMISSION_WILDCARD = "*";
1173
+ function permissionsAreWildcard(permissions) {
1174
+ return permissions !== void 0 && PERMISSION_WILDCARD in permissions;
1175
+ }
841
1176
  function hasWritePermission(keyring, collectionName) {
842
1177
  if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian") return true;
843
1178
  if (keyring.role === "viewer" || keyring.role === "client") return false;
1179
+ if (keyring.permissions[PERMISSION_WILDCARD] === "rw") return true;
844
1180
  return keyring.permissions[collectionName] === "rw";
845
1181
  }
846
1182
  function hasAccess(keyring, collectionName) {
847
1183
  if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian" || keyring.role === "viewer")
848
1184
  return true;
1185
+ if (PERMISSION_WILDCARD in keyring.permissions) return true;
849
1186
  return collectionName in keyring.permissions;
850
1187
  }
851
1188
  async function persistKeyring(store, vault, keyring) {
@@ -854,26 +1191,41 @@ async function persistKeyring(store, vault, keyring) {
854
1191
  "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."
855
1192
  );
856
1193
  }
1194
+ const rosterKey = requireRosterKey(keyring, "persistKeyring");
857
1195
  const existingFound = await readKeyringFile(store, vault, keyring.userId);
1196
+ if (existingFound) await assertRosterTagValid(existingFound.file, rosterKey, keyring.userId);
858
1197
  const existingEcho = existingFound?.file.echo;
1198
+ const existingGrantedBy = existingFound?.file.granted_by;
1199
+ const existingCreatedAt = existingFound?.file.created_at;
1200
+ const existingExpiresAt = existingFound?.file.expires_at;
859
1201
  const wrappedDeks = {};
860
1202
  for (const [collName, dek] of keyring.deks) {
861
1203
  wrappedDeks[collName] = await wrapKey(dek, keyring.kek);
862
1204
  }
1205
+ const wrappedPending = {};
1206
+ for (const [collName, dek] of keyring.pendingDeks ?? []) {
1207
+ wrappedPending[collName] = await wrapKey(dek, keyring.kek);
1208
+ }
863
1209
  const canary = await mintKeyringCanary(keyring.kek);
864
- const keyringFile = {
865
- _noydb_keyring: NOYDB_KEYRING_VERSION,
1210
+ const authority = {
866
1211
  user_id: keyring.userId,
867
1212
  display_name: keyring.displayName,
868
1213
  role: keyring.role,
869
1214
  permissions: keyring.permissions,
1215
+ granted_by: existingGrantedBy ?? keyring.userId,
1216
+ ...existingExpiresAt !== void 0 && { expires_at: existingExpiresAt },
1217
+ ...keyring.exportCapability !== void 0 && { export_capability: keyring.exportCapability },
1218
+ ...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability }
1219
+ };
1220
+ const keyringFile = {
1221
+ _noydb_keyring: NOYDB_KEYRING_VERSION,
1222
+ ...authority,
870
1223
  deks: wrappedDeks,
1224
+ ...Object.keys(wrappedPending).length > 0 ? { pending_deks: wrappedPending } : {},
871
1225
  salt: bufferToBase64(keyring.salt),
872
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
873
- granted_by: keyring.userId,
1226
+ created_at: existingCreatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
874
1227
  canary,
875
- ...keyring.exportCapability !== void 0 && { export_capability: keyring.exportCapability },
876
- ...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability },
1228
+ roster_tag: await mintRosterTag(authority, rosterKey),
877
1229
  ...keyring.authenticators.length > 0 && { authenticators: keyring.authenticators },
878
1230
  ...keyring.policy !== void 0 && { policy: keyring.policy },
879
1231
  ...existingEcho !== void 0 && { echo: existingEcho }
@@ -918,17 +1270,17 @@ function resolvePermissions(role, explicit) {
918
1270
  return explicit ?? {};
919
1271
  }
920
1272
  async function writeKeyringFile(store, vault, userId, keyringFile) {
921
- const envelope = {
922
- _noydb: 1,
923
- _v: 1,
924
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
925
- _iv: "",
926
- _data: JSON.stringify(keyringFile)
927
- };
1273
+ const envelope = buildRecordEnvelope(
1274
+ { collection: "_keyring", id: userId, version: 1 },
1275
+ { iv: "", data: JSON.stringify(keyringFile) }
1276
+ );
928
1277
  await store.put(vault, "_keyring", userId, envelope);
929
1278
  }
930
1279
 
931
1280
  export {
1281
+ mintRosterTag,
1282
+ assertRosterAuthenticated,
1283
+ assertRosterTagValid,
932
1284
  WeakSecretError,
933
1285
  validateSecret,
934
1286
  assertStrongSecret,
@@ -945,6 +1297,7 @@ export {
945
1297
  BROKER_COLLECTION,
946
1298
  isSecretBearingReservedCollection,
947
1299
  mintKeyringCanary,
1300
+ requireRosterKey,
948
1301
  deriveKekForKeyring,
949
1302
  readKeyringFile,
950
1303
  assertKeyringNotExpired,
@@ -953,6 +1306,8 @@ export {
953
1306
  createOwnerKeyring,
954
1307
  grant,
955
1308
  revoke,
1309
+ verifyRoster,
1310
+ quarantineKeyring,
956
1311
  updateKeyringIdentity,
957
1312
  rotateKeys,
958
1313
  changeSecret,
@@ -968,4 +1323,4 @@ export {
968
1323
  hasImportCapability,
969
1324
  evaluateImportCapability
970
1325
  };
971
- //# sourceMappingURL=chunk-ERB6NDDW.js.map
1326
+ //# sourceMappingURL=chunk-CAGI4LLJ.js.map