mindoodb 0.0.23 → 0.0.32

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 (340) hide show
  1. package/README-server.md +9 -1
  2. package/README.md +5 -3
  3. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +1 -1
  4. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
  5. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +24 -5
  6. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
  7. package/dist/appendonlystores/network/HttpTransport.d.ts +14 -2
  8. package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
  9. package/dist/appendonlystores/network/HttpTransport.js +124 -27
  10. package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
  11. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +159 -4
  12. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
  13. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +334 -25
  14. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
  15. package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts +9 -0
  16. package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts.map +1 -1
  17. package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js +77 -5
  18. package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js.map +1 -1
  19. package/dist/browser/index.mjs +103 -0
  20. package/dist/core/BaseMindooDB.d.ts +697 -4
  21. package/dist/core/BaseMindooDB.d.ts.map +1 -1
  22. package/dist/core/BaseMindooDB.js +3273 -180
  23. package/dist/core/BaseMindooDB.js.map +1 -1
  24. package/dist/core/BaseMindooTenant.d.ts +192 -1
  25. package/dist/core/BaseMindooTenant.d.ts.map +1 -1
  26. package/dist/core/BaseMindooTenant.js +633 -88
  27. package/dist/core/BaseMindooTenant.js.map +1 -1
  28. package/dist/core/BaseMindooTenantDirectory.d.ts +770 -10
  29. package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
  30. package/dist/core/BaseMindooTenantDirectory.js +2715 -292
  31. package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
  32. package/dist/core/BaseMindooTenantFactory.d.ts +2 -0
  33. package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
  34. package/dist/core/BaseMindooTenantFactory.js +20 -0
  35. package/dist/core/BaseMindooTenantFactory.js.map +1 -1
  36. package/dist/core/accesscontrol/AccessDeniedError.d.ts +22 -0
  37. package/dist/core/accesscontrol/AccessDeniedError.d.ts.map +1 -0
  38. package/dist/core/accesscontrol/AccessDeniedError.js +28 -0
  39. package/dist/core/accesscontrol/AccessDeniedError.js.map +1 -0
  40. package/dist/core/accesscontrol/DirectoryStateNode.d.ts +220 -0
  41. package/dist/core/accesscontrol/DirectoryStateNode.d.ts.map +1 -0
  42. package/dist/core/accesscontrol/DirectoryStateNode.js +297 -0
  43. package/dist/core/accesscontrol/DirectoryStateNode.js.map +1 -0
  44. package/dist/core/accesscontrol/DirectoryTimeTravelIndex.d.ts +118 -0
  45. package/dist/core/accesscontrol/DirectoryTimeTravelIndex.d.ts.map +1 -0
  46. package/dist/core/accesscontrol/DirectoryTimeTravelIndex.js +173 -0
  47. package/dist/core/accesscontrol/DirectoryTimeTravelIndex.js.map +1 -0
  48. package/dist/core/accesscontrol/directoryProjection.d.ts +26 -0
  49. package/dist/core/accesscontrol/directoryProjection.d.ts.map +1 -0
  50. package/dist/core/accesscontrol/directoryProjection.js +174 -0
  51. package/dist/core/accesscontrol/directoryProjection.js.map +1 -0
  52. package/dist/core/accesscontrol/evaluate.d.ts +77 -0
  53. package/dist/core/accesscontrol/evaluate.d.ts.map +1 -0
  54. package/dist/core/accesscontrol/evaluate.js +206 -0
  55. package/dist/core/accesscontrol/evaluate.js.map +1 -0
  56. package/dist/core/accesscontrol/grantKeys.d.ts +113 -0
  57. package/dist/core/accesscontrol/grantKeys.d.ts.map +1 -0
  58. package/dist/core/accesscontrol/grantKeys.js +302 -0
  59. package/dist/core/accesscontrol/grantKeys.js.map +1 -0
  60. package/dist/core/accesscontrol/keyBagReconciler.d.ts +22 -0
  61. package/dist/core/accesscontrol/keyBagReconciler.d.ts.map +1 -0
  62. package/dist/core/accesscontrol/keyBagReconciler.js +3 -0
  63. package/dist/core/accesscontrol/keyBagReconciler.js.map +1 -0
  64. package/dist/core/accesscontrol/materializationGuard.d.ts +89 -0
  65. package/dist/core/accesscontrol/materializationGuard.d.ts.map +1 -0
  66. package/dist/core/accesscontrol/materializationGuard.js +69 -0
  67. package/dist/core/accesscontrol/materializationGuard.js.map +1 -0
  68. package/dist/core/accesscontrol/quarantine.d.ts +65 -0
  69. package/dist/core/accesscontrol/quarantine.d.ts.map +1 -0
  70. package/dist/core/accesscontrol/quarantine.js +79 -0
  71. package/dist/core/accesscontrol/quarantine.js.map +1 -0
  72. package/dist/core/accesscontrol/receiptValidation.d.ts +113 -0
  73. package/dist/core/accesscontrol/receiptValidation.d.ts.map +1 -0
  74. package/dist/core/accesscontrol/receiptValidation.js +142 -0
  75. package/dist/core/accesscontrol/receiptValidation.js.map +1 -0
  76. package/dist/core/accesscontrol/timestamp/Ed25519WitnessProvider.d.ts +31 -0
  77. package/dist/core/accesscontrol/timestamp/Ed25519WitnessProvider.d.ts.map +1 -0
  78. package/dist/core/accesscontrol/timestamp/Ed25519WitnessProvider.js +66 -0
  79. package/dist/core/accesscontrol/timestamp/Ed25519WitnessProvider.js.map +1 -0
  80. package/dist/core/accesscontrol/timestamp/TimestampProvider.d.ts +79 -0
  81. package/dist/core/accesscontrol/timestamp/TimestampProvider.d.ts.map +1 -0
  82. package/dist/core/accesscontrol/timestamp/TimestampProvider.js +22 -0
  83. package/dist/core/accesscontrol/timestamp/TimestampProvider.js.map +1 -0
  84. package/dist/core/accesscontrol/types.d.ts +825 -0
  85. package/dist/core/accesscontrol/types.d.ts.map +1 -0
  86. package/dist/core/accesscontrol/types.js +534 -0
  87. package/dist/core/accesscontrol/types.js.map +1 -0
  88. package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts +9 -0
  89. package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts.map +1 -1
  90. package/dist/core/appendonlystores/InMemoryContentAddressedStore.js +54 -0
  91. package/dist/core/appendonlystores/InMemoryContentAddressedStore.js.map +1 -1
  92. package/dist/core/appendonlystores/network/AuthenticationService.d.ts +38 -2
  93. package/dist/core/appendonlystores/network/AuthenticationService.d.ts.map +1 -1
  94. package/dist/core/appendonlystores/network/AuthenticationService.js +199 -26
  95. package/dist/core/appendonlystores/network/AuthenticationService.js.map +1 -1
  96. package/dist/core/appendonlystores/network/NetworkTransport.d.ts +17 -3
  97. package/dist/core/appendonlystores/network/NetworkTransport.d.ts.map +1 -1
  98. package/dist/core/appendonlystores/network/types.d.ts +58 -4
  99. package/dist/core/appendonlystores/network/types.d.ts.map +1 -1
  100. package/dist/core/appendonlystores/network/types.js +4 -0
  101. package/dist/core/appendonlystores/network/types.js.map +1 -1
  102. package/dist/core/appendonlystores/scanUtils.d.ts +27 -0
  103. package/dist/core/appendonlystores/scanUtils.d.ts.map +1 -0
  104. package/dist/core/appendonlystores/scanUtils.js +87 -0
  105. package/dist/core/appendonlystores/scanUtils.js.map +1 -0
  106. package/dist/core/appendonlystores/types.d.ts +23 -2
  107. package/dist/core/appendonlystores/types.d.ts.map +1 -1
  108. package/dist/core/cache/CacheManager.d.ts +13 -3
  109. package/dist/core/cache/CacheManager.d.ts.map +1 -1
  110. package/dist/core/cache/CacheManager.js +7 -4
  111. package/dist/core/cache/CacheManager.js.map +1 -1
  112. package/dist/core/crypto/EntrySignature.d.ts +137 -0
  113. package/dist/core/crypto/EntrySignature.d.ts.map +1 -0
  114. package/dist/core/crypto/EntrySignature.js +210 -0
  115. package/dist/core/crypto/EntrySignature.js.map +1 -0
  116. package/dist/core/crypto/MindooDocSigner.d.ts.map +1 -1
  117. package/dist/core/crypto/MindooDocSigner.js +6 -2
  118. package/dist/core/crypto/MindooDocSigner.js.map +1 -1
  119. package/dist/core/crypto/WitnessReceipt.d.ts +137 -0
  120. package/dist/core/crypto/WitnessReceipt.d.ts.map +1 -0
  121. package/dist/core/crypto/WitnessReceipt.js +182 -0
  122. package/dist/core/crypto/WitnessReceipt.js.map +1 -0
  123. package/dist/core/crypto/encryptedFields.d.ts +27 -0
  124. package/dist/core/crypto/encryptedFields.d.ts.map +1 -0
  125. package/dist/core/crypto/encryptedFields.js +58 -0
  126. package/dist/core/crypto/encryptedFields.js.map +1 -0
  127. package/dist/core/crypto/pbkdf2Iterations.d.ts +12 -0
  128. package/dist/core/crypto/pbkdf2Iterations.d.ts.map +1 -1
  129. package/dist/core/crypto/pbkdf2Iterations.js +23 -0
  130. package/dist/core/crypto/pbkdf2Iterations.js.map +1 -1
  131. package/dist/core/crypto/privateKeyEncryption.d.ts.map +1 -1
  132. package/dist/core/crypto/privateKeyEncryption.js +3 -1
  133. package/dist/core/crypto/privateKeyEncryption.js.map +1 -1
  134. package/dist/core/databaseIdValidation.d.ts +1 -1
  135. package/dist/core/databaseIdValidation.d.ts.map +1 -1
  136. package/dist/core/databaseIdValidation.js +4 -4
  137. package/dist/core/databaseIdValidation.js.map +1 -1
  138. package/dist/core/errors.d.ts +26 -0
  139. package/dist/core/errors.d.ts.map +1 -1
  140. package/dist/core/errors.js +42 -1
  141. package/dist/core/errors.js.map +1 -1
  142. package/dist/core/expressions/builder.d.ts +102 -0
  143. package/dist/core/expressions/builder.d.ts.map +1 -0
  144. package/dist/core/expressions/builder.js +249 -0
  145. package/dist/core/expressions/builder.js.map +1 -0
  146. package/dist/core/expressions/evaluateExpression.d.ts +78 -0
  147. package/dist/core/expressions/evaluateExpression.d.ts.map +1 -0
  148. package/dist/core/expressions/evaluateExpression.js +386 -0
  149. package/dist/core/expressions/evaluateExpression.js.map +1 -0
  150. package/dist/core/expressions/formulaSource.d.ts +24 -0
  151. package/dist/core/expressions/formulaSource.d.ts.map +1 -0
  152. package/dist/core/expressions/formulaSource.js +765 -0
  153. package/dist/core/expressions/formulaSource.js.map +1 -0
  154. package/dist/core/expressions/index.d.ts +8 -0
  155. package/dist/core/expressions/index.d.ts.map +1 -0
  156. package/dist/core/expressions/index.js +46 -0
  157. package/dist/core/expressions/index.js.map +1 -0
  158. package/dist/core/expressions/metadata.d.ts +23 -0
  159. package/dist/core/expressions/metadata.d.ts.map +1 -0
  160. package/dist/core/expressions/metadata.js +191 -0
  161. package/dist/core/expressions/metadata.js.map +1 -0
  162. package/dist/core/expressions/types.d.ts +164 -0
  163. package/dist/core/expressions/types.d.ts.map +1 -0
  164. package/dist/core/expressions/types.js +3 -0
  165. package/dist/core/expressions/types.js.map +1 -0
  166. package/dist/core/extraction/types.d.ts +47 -0
  167. package/dist/core/extraction/types.d.ts.map +1 -0
  168. package/dist/core/extraction/types.js +42 -0
  169. package/dist/core/extraction/types.js.map +1 -0
  170. package/dist/core/index.d.ts +13 -3
  171. package/dist/core/index.d.ts.map +1 -1
  172. package/dist/core/index.js +113 -4
  173. package/dist/core/index.js.map +1 -1
  174. package/dist/core/indexing/createViewDataProvider.d.ts +84 -0
  175. package/dist/core/indexing/createViewDataProvider.d.ts.map +1 -0
  176. package/dist/core/indexing/createViewDataProvider.js +139 -0
  177. package/dist/core/indexing/createViewDataProvider.js.map +1 -0
  178. package/dist/core/indexing/fulltext/DocumentFullTextIndex.d.ts +180 -0
  179. package/dist/core/indexing/fulltext/DocumentFullTextIndex.d.ts.map +1 -0
  180. package/dist/core/indexing/fulltext/DocumentFullTextIndex.js +548 -0
  181. package/dist/core/indexing/fulltext/DocumentFullTextIndex.js.map +1 -0
  182. package/dist/core/indexing/fulltext/SearchEngineAdapter.d.ts +69 -0
  183. package/dist/core/indexing/fulltext/SearchEngineAdapter.d.ts.map +1 -0
  184. package/dist/core/indexing/fulltext/SearchEngineAdapter.js +183 -0
  185. package/dist/core/indexing/fulltext/SearchEngineAdapter.js.map +1 -0
  186. package/dist/core/indexing/fulltext/extractFulltextText.d.ts +34 -0
  187. package/dist/core/indexing/fulltext/extractFulltextText.d.ts.map +1 -0
  188. package/dist/core/indexing/fulltext/extractFulltextText.js +129 -0
  189. package/dist/core/indexing/fulltext/extractFulltextText.js.map +1 -0
  190. package/dist/core/indexing/fulltext/index.d.ts +5 -0
  191. package/dist/core/indexing/fulltext/index.d.ts.map +1 -0
  192. package/dist/core/indexing/fulltext/index.js +20 -0
  193. package/dist/core/indexing/fulltext/index.js.map +1 -0
  194. package/dist/core/indexing/fulltext/types.d.ts +168 -0
  195. package/dist/core/indexing/fulltext/types.d.ts.map +1 -0
  196. package/dist/core/indexing/fulltext/types.js +89 -0
  197. package/dist/core/indexing/fulltext/types.js.map +1 -0
  198. package/dist/core/indexing/summary/DocumentSummaryStore.d.ts +137 -0
  199. package/dist/core/indexing/summary/DocumentSummaryStore.d.ts.map +1 -0
  200. package/dist/core/indexing/summary/DocumentSummaryStore.js +438 -0
  201. package/dist/core/indexing/summary/DocumentSummaryStore.js.map +1 -0
  202. package/dist/core/indexing/summary/SummaryVirtualViewDataProvider.d.ts +72 -0
  203. package/dist/core/indexing/summary/SummaryVirtualViewDataProvider.d.ts.map +1 -0
  204. package/dist/core/indexing/summary/SummaryVirtualViewDataProvider.js +221 -0
  205. package/dist/core/indexing/summary/SummaryVirtualViewDataProvider.js.map +1 -0
  206. package/dist/core/indexing/summary/extractSummaryFields.d.ts +75 -0
  207. package/dist/core/indexing/summary/extractSummaryFields.d.ts.map +1 -0
  208. package/dist/core/indexing/summary/extractSummaryFields.js +266 -0
  209. package/dist/core/indexing/summary/extractSummaryFields.js.map +1 -0
  210. package/dist/core/indexing/summary/index.d.ts +5 -0
  211. package/dist/core/indexing/summary/index.d.ts.map +1 -0
  212. package/dist/core/indexing/summary/index.js +23 -0
  213. package/dist/core/indexing/summary/index.js.map +1 -0
  214. package/dist/core/indexing/summary/types.d.ts +119 -0
  215. package/dist/core/indexing/summary/types.d.ts.map +1 -0
  216. package/dist/core/indexing/summary/types.js +81 -0
  217. package/dist/core/indexing/summary/types.js.map +1 -0
  218. package/dist/core/indexing/virtualviews/IVirtualViewDataProvider.d.ts +47 -1
  219. package/dist/core/indexing/virtualviews/IVirtualViewDataProvider.d.ts.map +1 -1
  220. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.d.ts +24 -8
  221. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.d.ts.map +1 -1
  222. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.js +96 -41
  223. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.js.map +1 -1
  224. package/dist/core/indexing/virtualviews/ViewEntrySortKey.d.ts +2 -0
  225. package/dist/core/indexing/virtualviews/ViewEntrySortKey.d.ts.map +1 -1
  226. package/dist/core/indexing/virtualviews/ViewEntrySortKey.js +8 -3
  227. package/dist/core/indexing/virtualviews/ViewEntrySortKey.js.map +1 -1
  228. package/dist/core/indexing/virtualviews/VirtualView.d.ts +111 -5
  229. package/dist/core/indexing/virtualviews/VirtualView.d.ts.map +1 -1
  230. package/dist/core/indexing/virtualviews/VirtualView.js +266 -75
  231. package/dist/core/indexing/virtualviews/VirtualView.js.map +1 -1
  232. package/dist/core/indexing/virtualviews/VirtualViewColumn.d.ts +12 -0
  233. package/dist/core/indexing/virtualviews/VirtualViewColumn.d.ts.map +1 -1
  234. package/dist/core/indexing/virtualviews/VirtualViewColumn.js +1 -0
  235. package/dist/core/indexing/virtualviews/VirtualViewColumn.js.map +1 -1
  236. package/dist/core/indexing/virtualviews/VirtualViewEntryData.d.ts +12 -0
  237. package/dist/core/indexing/virtualviews/VirtualViewEntryData.d.ts.map +1 -1
  238. package/dist/core/indexing/virtualviews/VirtualViewEntryData.js +66 -5
  239. package/dist/core/indexing/virtualviews/VirtualViewEntryData.js.map +1 -1
  240. package/dist/core/indexing/virtualviews/VirtualViewFactory.d.ts +50 -6
  241. package/dist/core/indexing/virtualviews/VirtualViewFactory.d.ts.map +1 -1
  242. package/dist/core/indexing/virtualviews/VirtualViewFactory.js +54 -12
  243. package/dist/core/indexing/virtualviews/VirtualViewFactory.js.map +1 -1
  244. package/dist/core/indexing/virtualviews/index.d.ts +3 -3
  245. package/dist/core/indexing/virtualviews/index.d.ts.map +1 -1
  246. package/dist/core/indexing/virtualviews/index.js.map +1 -1
  247. package/dist/core/indexing/virtualviews/types.d.ts +6 -0
  248. package/dist/core/indexing/virtualviews/types.d.ts.map +1 -1
  249. package/dist/core/indexing/virtualviews/types.js +11 -3
  250. package/dist/core/indexing/virtualviews/types.js.map +1 -1
  251. package/dist/core/keys/KeyBag.d.ts +25 -0
  252. package/dist/core/keys/KeyBag.d.ts.map +1 -1
  253. package/dist/core/keys/KeyBag.js +37 -1
  254. package/dist/core/keys/KeyBag.js.map +1 -1
  255. package/dist/core/query/executeQuery.d.ts +18 -0
  256. package/dist/core/query/executeQuery.d.ts.map +1 -0
  257. package/dist/core/query/executeQuery.js +313 -0
  258. package/dist/core/query/executeQuery.js.map +1 -0
  259. package/dist/core/query/index.d.ts +5 -0
  260. package/dist/core/query/index.d.ts.map +1 -0
  261. package/dist/core/query/index.js +14 -0
  262. package/dist/core/query/index.js.map +1 -0
  263. package/dist/core/query/queryLive.d.ts +27 -0
  264. package/dist/core/query/queryLive.d.ts.map +1 -0
  265. package/dist/core/query/queryLive.js +104 -0
  266. package/dist/core/query/queryLive.js.map +1 -0
  267. package/dist/core/query/queryView.d.ts +156 -0
  268. package/dist/core/query/queryView.d.ts.map +1 -0
  269. package/dist/core/query/queryView.js +266 -0
  270. package/dist/core/query/queryView.js.map +1 -0
  271. package/dist/core/query/types.d.ts +121 -0
  272. package/dist/core/query/types.d.ts.map +1 -0
  273. package/dist/core/query/types.js +18 -0
  274. package/dist/core/query/types.js.map +1 -0
  275. package/dist/core/storeEntryTime.d.ts +57 -0
  276. package/dist/core/storeEntryTime.d.ts.map +1 -0
  277. package/dist/core/storeEntryTime.js +72 -0
  278. package/dist/core/storeEntryTime.js.map +1 -0
  279. package/dist/core/types.d.ts +1509 -39
  280. package/dist/core/types.d.ts.map +1 -1
  281. package/dist/core/types.js +13 -1
  282. package/dist/core/types.js.map +1 -1
  283. package/dist/core/uri/MindooURI.d.ts +53 -1
  284. package/dist/core/uri/MindooURI.d.ts.map +1 -1
  285. package/dist/core/uri/MindooURI.js +135 -0
  286. package/dist/core/uri/MindooURI.js.map +1 -1
  287. package/dist/core/utils/urlSafety.d.ts +46 -0
  288. package/dist/core/utils/urlSafety.d.ts.map +1 -0
  289. package/dist/core/utils/urlSafety.js +144 -0
  290. package/dist/core/utils/urlSafety.js.map +1 -0
  291. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +14 -0
  292. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -1
  293. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +95 -4
  294. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -1
  295. package/dist/node/server/CapabilityMatcher.d.ts +10 -0
  296. package/dist/node/server/CapabilityMatcher.d.ts.map +1 -1
  297. package/dist/node/server/CapabilityMatcher.js +14 -2
  298. package/dist/node/server/CapabilityMatcher.js.map +1 -1
  299. package/dist/node/server/MindooDBServer.d.ts.map +1 -1
  300. package/dist/node/server/MindooDBServer.js +117 -8
  301. package/dist/node/server/MindooDBServer.js.map +1 -1
  302. package/dist/node/server/PurgedDocRegistry.d.ts +32 -0
  303. package/dist/node/server/PurgedDocRegistry.d.ts.map +1 -0
  304. package/dist/node/server/PurgedDocRegistry.js +94 -0
  305. package/dist/node/server/PurgedDocRegistry.js.map +1 -0
  306. package/dist/node/server/ServerSync.d.ts.map +1 -1
  307. package/dist/node/server/ServerSync.js +5 -2
  308. package/dist/node/server/ServerSync.js.map +1 -1
  309. package/dist/node/server/SystemAdminAuth.d.ts +10 -0
  310. package/dist/node/server/SystemAdminAuth.d.ts.map +1 -1
  311. package/dist/node/server/SystemAdminAuth.js +25 -0
  312. package/dist/node/server/SystemAdminAuth.js.map +1 -1
  313. package/dist/node/server/TenantManager.d.ts +95 -1
  314. package/dist/node/server/TenantManager.d.ts.map +1 -1
  315. package/dist/node/server/TenantManager.js +382 -15
  316. package/dist/node/server/TenantManager.js.map +1 -1
  317. package/dist/node/server/config.d.ts.map +1 -1
  318. package/dist/node/server/config.js +5 -1
  319. package/dist/node/server/config.js.map +1 -1
  320. package/dist/node/server/types.d.ts +29 -3
  321. package/dist/node/server/types.d.ts.map +1 -1
  322. package/dist/node/server/types.js +12 -0
  323. package/dist/node/server/types.js.map +1 -1
  324. package/dist/node/server/validation.d.ts +6 -0
  325. package/dist/node/server/validation.d.ts.map +1 -1
  326. package/dist/node/server/validation.js +7 -1
  327. package/dist/node/server/validation.js.map +1 -1
  328. package/package.json +4 -3
  329. package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts +0 -8
  330. package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts.map +0 -1
  331. package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js +0 -16
  332. package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js.map +0 -1
  333. package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +0 -68
  334. package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +0 -1
  335. package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js +0 -659
  336. package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js.map +0 -1
  337. package/dist/node/attachments/writeAttachmentToFile.d.ts +0 -15
  338. package/dist/node/attachments/writeAttachmentToFile.d.ts.map +0 -1
  339. package/dist/node/attachments/writeAttachmentToFile.js +0 -73
  340. package/dist/node/attachments/writeAttachmentToFile.js.map +0 -1
@@ -1,30 +1,636 @@
1
- import { type DirectoryUserDetails, type DirectoryUserLookup, EncryptedPrivateKey, MindooDB, MindooDoc, MindooTenantDirectory, PublicUserId } from "./types";
1
+ import { type DirectoryUserDetails, type DirectoryUserLookup, type GrantKeyPair, type GrantKeyPairInfo, EncryptedPrivateKey, MindooDB, MindooDoc, MindooTenantDirectory, PublicUserId } from "./types";
2
2
  import { BaseMindooTenant } from "./BaseMindooTenant";
3
3
  import { Logger } from "./logging";
4
- export declare class BaseMindooTenantDirectory implements MindooTenantDirectory {
4
+ import { KeyBagReconciler } from "./accesscontrol/keyBagReconciler";
5
+ import { DirectoryStateNode } from "./accesscontrol/DirectoryStateNode";
6
+ import { AclRuleDoc, DefaultAccessPolicyDoc, KeyDistributionPushRecipient, KeyDistributionRequest, KeyDistributionView, KeyVersionRef, RuleTargets, RuleType, WithFieldClause, AppDistributionRequest, AppDistributionView, AppDistributionReconcilePlan, SyncSetupPolicyRequest, SyncSetupPolicyView, SyncSetupPolicyReconcilePlan, DocHistoryPurgeRequest, DocHistoryPurgeView } from "./accesscontrol/types";
7
+ import { IdentitySet } from "./accesscontrol/evaluate";
8
+ import { AccessDecision } from "./accesscontrol/types";
9
+ export declare class BaseMindooTenantDirectory implements MindooTenantDirectory, KeyBagReconciler {
5
10
  private tenant;
6
11
  private directoryDB;
7
12
  private trustedKeysCache;
8
- private grantDocIdToPublicKey;
13
+ private grantDocIdToSigningKeys;
9
14
  private userLookupCache;
15
+ private wipeKeyToGrantDocId;
10
16
  private tenantSettingsCache;
11
17
  private dbSettingsCache;
12
18
  private groupsCache;
19
+ private keyDistributionCache;
20
+ private appDistributionCache;
21
+ private syncSetupPolicyCache;
22
+ private timeTravel;
23
+ private lastTimeTravelChangeSeq;
13
24
  private unifiedCacheLastCursor;
14
25
  private lastDirectorySyncTimestamp;
26
+ private metadataSignatureCutoffCache;
27
+ private metadataSignatureCutoffCachedAt;
28
+ private resolvingMetadataSignatureCutoff;
15
29
  private logger;
16
30
  constructor(tenant: BaseMindooTenant, logger?: Logger);
17
31
  getDirectoryDB(): Promise<MindooDB>;
18
- registerUser(userId: PublicUserId, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string, userDetails?: DirectoryUserDetails): Promise<void>;
32
+ registerUser(userId: PublicUserId, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string, userDetails?: DirectoryUserDetails, label?: string): Promise<void>;
19
33
  findGrantAccessDocuments(username: string): Promise<MindooDoc[]>;
20
- revokeUser(username: string, requestDataWipe: boolean, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
21
- validatePublicSigningKey(publicKey: string): Promise<boolean>;
34
+ /**
35
+ * Revoke a user's access by removing their keys from the grant document
36
+ * (docs/accesscontrol.md §6.5). This replaces the legacy `revokeaccess`
37
+ * document model entirely: revocation is now an in-place edit of the grant
38
+ * document's `userSigningPublicKeys` / `userEncryptionPublicKeys` arrays.
39
+ *
40
+ * By default (no `options.signingKeys`) the user is fully revoked: every
41
+ * signing AND encryption key is removed, so the grant document remains but
42
+ * carries no keys. To revoke a single device/key, pass the specific
43
+ * `signingKeys` (and optionally matching `encryptionKeys`) to remove.
44
+ *
45
+ * When `options.requestDataWipe` is true, the removed signing keys are also
46
+ * flagged for a remote device wipe (§6.5) before removal, so the targeted
47
+ * devices delete their local tenant on next sync. The wipe directive is
48
+ * stored self-contained on the grant document and survives key removal.
49
+ *
50
+ * BREAKING CHANGE: the previous signature took a positional `requestDataWipe`
51
+ * boolean; it now takes an options object so specific keys can be targeted.
52
+ *
53
+ * @param username The user to revoke (format: "CN=<username>/O=<tenantId>").
54
+ * @param options.signingKeys Specific signing keys to remove; omit/empty to
55
+ * remove all of the user's keys (full revocation).
56
+ * @param options.encryptionKeys Specific encryption keys to remove. Ignored
57
+ * for a full revocation (all encryption keys are removed in that case).
58
+ * @param options.requestDataWipe Also request a remote wipe of the removed
59
+ * signing keys' devices.
60
+ */
61
+ revokeUser(username: string, options: {
62
+ signingKeys?: string[];
63
+ encryptionKeys?: string[];
64
+ requestDataWipe?: boolean;
65
+ }, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
66
+ validatePublicSigningKey(publicKey: string, opts?: {
67
+ forceRefresh?: boolean;
68
+ }): Promise<boolean>;
22
69
  /**
23
70
  * Update all caches (trusted keys, groups, settings) by processing new changes since the last cursor.
24
71
  * This unified method processes all document types in a single loop for efficiency.
25
72
  * No signature verification is needed since the DB already enforces admin-only access.
26
73
  */
27
74
  private updateUnifiedCache;
75
+ /**
76
+ * The head time-travel directory-state node ("now"), after ensuring the chain
77
+ * is current. Powers access-control evaluation (§7) and audit queries.
78
+ */
79
+ getDirectoryStateHead(): Promise<DirectoryStateNode>;
80
+ /**
81
+ * The time-travel directory-state node covering trusted time `T` (§8) — used
82
+ * by the evaluation algorithm and `wasAllowedAt` to judge an entry against the
83
+ * directory as it was at the entry's trusted time.
84
+ */
85
+ getDirectoryStateAt(T: number): Promise<DirectoryStateNode>;
86
+ /**
87
+ * Lazily create the time-travel index (restoring its persisted delta log and
88
+ * registering it with the tenant CacheManager on first use), then bring the
89
+ * directory-state chain up to date from the revision-grain changefeed
90
+ * (docs/accesscontrol.md §8).
91
+ *
92
+ * The (potentially expensive) feed rebuild is skipped when the directory's
93
+ * changefeed cursor has not advanced since the previous build and no
94
+ * un-witnessed revisions are pending a possible trusted-time re-stamp.
95
+ */
96
+ private ensureTimeTravelCurrent;
97
+ /**
98
+ * The set of witness public keys trusted at trusted time `T`
99
+ * (docs/accesscontrol.md §6.4), used by the client to validate witness
100
+ * receipts on incoming entries during materialization (§5.4).
101
+ */
102
+ getTrustedWitnessKeysAt(T: number): Promise<Set<string>>;
103
+ /**
104
+ * Resolve the acting user's identity set for access-control evaluation
105
+ * (docs/accesscontrol.md §7 step 3): username hash (v1 + v2), every group hash
106
+ * (including nested groups), the user's resolved usernames/groups for
107
+ * placeholder expansion, and the applicable pseudo-tokens (`$everyone` always;
108
+ * `$admin`/`$author` when the caller indicates they apply).
109
+ *
110
+ * @param username The acting user's canonical username.
111
+ * @param opts.isAdmin Whether the acting signing key is the tenant admin key.
112
+ * @param opts.isAuthor Whether the acting user authored the target document
113
+ * (the caller resolves this by mapping the document's creator key and the
114
+ * change signer key to the same grant; see §6.3 `$author`).
115
+ */
116
+ buildIdentitySet(username: string, opts?: {
117
+ isAdmin?: boolean;
118
+ isAuthor?: boolean;
119
+ }): Promise<IdentitySet>;
120
+ /**
121
+ * Server-side Tier 1 evaluation for a pushed entry (docs/accesscontrol.md §7,
122
+ * §10). The server identifies the author by signing key (it cannot read
123
+ * encrypted content), resolves their identity set, and evaluates the identity
124
+ * tier of the policy at `trustedTime`. Tier 2 (content) rules are deferred to
125
+ * clients — `evaluateAccess` is called with `isServer: true`, so a gate that
126
+ * only a Tier 2 rule could decide is treated as allowed here.
127
+ *
128
+ * @param input.op The operation (entry type) being pushed.
129
+ * @param input.dbid The database the entry targets (witness binds this).
130
+ * @param input.signingKey The author's Ed25519 signing public key (PEM).
131
+ * @param input.trustedTime The trusted time to evaluate at (the witness's
132
+ * acceptance time for an inbound push).
133
+ * @param input.isAuthor Whether the author is the document's original creator
134
+ * (resolved by the caller for `$author`; see §6.3). For `doc_create` the
135
+ * author is always the creator.
136
+ */
137
+ evaluateAccessForSigningKey(input: {
138
+ op: RuleType;
139
+ dbid: string;
140
+ signingKey: string;
141
+ trustedTime: number;
142
+ isAuthor?: boolean;
143
+ }): Promise<AccessDecision>;
144
+ /**
145
+ * Client-side (Tier 1 + Tier 2) evaluation for a single entry during
146
+ * materialization (docs/accesscontrol.md §7, §10). Unlike the server path,
147
+ * the client can read decrypted content, so it evaluates `withfields` clauses
148
+ * against the supplied `before`/`after` document states.
149
+ */
150
+ evaluateClientAccess(input: {
151
+ op: RuleType;
152
+ dbid: string;
153
+ signingKey: string;
154
+ trustedTime: number;
155
+ isAuthor: boolean;
156
+ beforeDoc: Record<string, unknown> | null;
157
+ afterDoc: Record<string, unknown> | null;
158
+ }): Promise<AccessDecision>;
159
+ /**
160
+ * Whether the active policy has any Tier 2 (`withfields`) content rule for
161
+ * operation `op` in database `dbid` at the current head state. Lets the
162
+ * client write prechecks skip the (potentially costly) Automerge -> JS
163
+ * materialization of the before/after document when only Tier 1
164
+ * (identity/op) checks could apply (docs/accesscontrol.md §9).
165
+ */
166
+ hasWriteContentRules(op: RuleType, dbid: string): Promise<boolean>;
167
+ /**
168
+ * Whether access control is active for this tenant right now: a default
169
+ * policy exists and the master kill-switch is not engaged
170
+ * (docs/accesscontrol.md §6.1, §7 step 0). When false, callers can skip the
171
+ * (more expensive) per-entry access evaluation entirely.
172
+ */
173
+ isAccessControlActive(): Promise<boolean>;
174
+ /**
175
+ * The effective default `decryptionKeyId` for a `doc_create` in `dbid` at the
176
+ * current head state, or `undefined` when none is configured. Lets the client
177
+ * fill in the key when the caller omits one, instead of falling back to the
178
+ * hardcoded `"default"`. This is a create-time convenience, not a security
179
+ * control — which keys a user may create/read under is governed by key
180
+ * possession (the key distribution model, docs/accesscontrol.md §13).
181
+ */
182
+ getEffectiveDefaultCreateKeyId(dbid: string): Promise<string | undefined>;
183
+ /**
184
+ * The tenant-wide database-open policy at the current head state.
185
+ *
186
+ * This is a tenant-level control read only from the `acl_defaultpolicy`
187
+ * document (never layered per-db). When no default policy exists (access
188
+ * control off), or the field is omitted, the policy is `"open"`. In
189
+ * `"directory-restricted"` mode only `"directory"` (always implicitly
190
+ * allowed) and the returned `allowedDbIds` may be opened/synced.
191
+ */
192
+ getDatabaseCreationPolicy(): Promise<{
193
+ mode: "open" | "directory-restricted";
194
+ allowedDbIds: string[];
195
+ }>;
196
+ /**
197
+ * The tenant-wide storage-format floor (`requireMetadataSignatureSince`): the
198
+ * trusted-time cutoff at/after which entries must carry the v2 metadata-binding
199
+ * signature, or `undefined` when no floor is configured (fully backward
200
+ * compatible). Tenant-level only, read from the `acl_defaultpolicy` head.
201
+ *
202
+ * Cached with a short TTL and guarded against re-entrancy: resolving the
203
+ * cutoff loads the directory head, and directory materialization itself runs
204
+ * signature verification — without the guard a verify→resolve→materialize→
205
+ * verify cycle could recurse. During such re-entry we return the last known
206
+ * value (the directory store is anyway exempt from the floor by its callers).
207
+ */
208
+ getRequireMetadataSignatureSince(): Promise<number | undefined>;
209
+ /**
210
+ * Whether `dbid` may be opened/synced under the current database-open policy.
211
+ *
212
+ * Always true for `"directory"` and in `"open"` mode. In
213
+ * `"directory-restricted"` mode, true only when `dbid` is in `allowedDbIds`
214
+ * or the optional `signingKey` is the tenant administration key (admin
215
+ * bypass).
216
+ */
217
+ isDatabaseAllowed(dbid: string, opts?: {
218
+ signingKey?: string;
219
+ }): Promise<boolean>;
220
+ /**
221
+ * Server-oriented variant of {@link isDatabaseAllowed} that resolves the
222
+ * admin bypass from a request principal's signing key. Used by the sync
223
+ * server to gate all sync operations for a database id.
224
+ */
225
+ evaluateDbAccessForSigningKey(input: {
226
+ dbid: string;
227
+ signingKey: string;
228
+ }): Promise<boolean>;
229
+ /**
230
+ * Whether the current user may open and sync `dbid` under the database read
231
+ * gate (`doc_read`, §6.6). The client-side counterpart of
232
+ * {@link evaluateDbAccessForSigningKey}'s read gate: it evaluates the
233
+ * `doc_read` policy for the current user against the directory head. The
234
+ * `"directory"` database is never gated and the tenant admin is always
235
+ * exempt. This does NOT apply the tenant-wide database-id allowlist
236
+ * ({@link isDatabaseAllowed}); callers enforce both gates.
237
+ */
238
+ canReadDatabase(dbid: string): Promise<boolean>;
239
+ /**
240
+ * Create or update the tenant-wide default policy (§6.1). The mere existence
241
+ * of this document activates access control. Only the fields supplied are
242
+ * written; omit a field to leave it unset (interpreted as its default).
243
+ */
244
+ setDefaultAccessPolicy(policy: Partial<Omit<DefaultAccessPolicyDoc, "form" | "type">>, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
245
+ /**
246
+ * Create or update a per-database policy override (§6.2). Fields set here
247
+ * override the tenant default for `dbid`; unset fields inherit it.
248
+ */
249
+ setDatabaseAccessPolicy(dbid: string, policy: Partial<Omit<DefaultAccessPolicyDoc, "form" | "type">>, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
250
+ /** Shared writer for default / per-db policy documents. */
251
+ private writePolicyDoc;
252
+ /**
253
+ * Create or replace an access-control rule (§6.3). A rule with `withfields`
254
+ * is Tier 2 (client-enforced); otherwise it is Tier 1 (server-enforced). The
255
+ * spec is validated before writing.
256
+ */
257
+ createAccessRule(rule: {
258
+ ruleId: string;
259
+ type: RuleType;
260
+ dbid?: string;
261
+ action?: "allow" | "deny";
262
+ users_hashes?: string[];
263
+ /** Usernames to target; resolved to their (salted v2) hashes. */
264
+ usernames?: string[];
265
+ /** Group names to target; resolved to their (salted v2) hashes. */
266
+ groups?: string[];
267
+ withfields?: WithFieldClause[];
268
+ description?: string;
269
+ }, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
270
+ /**
271
+ * List access-control rules currently in effect (§9), optionally filtered by
272
+ * operation type and/or database. Rules with `dbid: "*"` match any database.
273
+ */
274
+ listRules(filter?: {
275
+ type?: RuleType;
276
+ dbid?: string;
277
+ }): Promise<Array<AclRuleDoc & {
278
+ targets?: RuleTargets;
279
+ }>>;
280
+ /** Delete an access-control rule by id (§9): a soft-delete of its document. */
281
+ deleteRule(ruleId: string, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
282
+ /**
283
+ * Add (or refresh) a trusted witness (§6.4). The witness document is keyed by
284
+ * a fingerprint of its public key so add/remove are symmetric.
285
+ */
286
+ addTrustedWitness(witness: {
287
+ witnessPublicKey: string;
288
+ serverUrl?: string;
289
+ }, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
290
+ /** Remove a trusted witness by its public key (§6.4): soft-deletes its document. */
291
+ removeTrustedWitness(witnessPublicKey: string, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
292
+ /**
293
+ * Add device key pairs to a user's grant (§6.5 key rollover / new device).
294
+ * Pairs are merged by signing key (a pair with an already-present signing key
295
+ * updates its encryption key/label). See {@link mergeKeyPairs}.
296
+ */
297
+ addUserKeys(username: string, keyPairs: GrantKeyPair[], administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
298
+ /**
299
+ * Revoke keys from a user's grant (§6.5 revoke a device/key). A device is
300
+ * identified by its signing key; revoking a signing key marks its entire
301
+ * paired entry revoked. `encryptionKeys` additionally targets any pair whose
302
+ * encryption key matches (kept for callers that target encryption keys
303
+ * directly).
304
+ *
305
+ * Revoked pairs are RETAINED on the grant document with `revoked: true` and a
306
+ * `revokedAt` timestamp (instead of being deleted), so admin UIs can list and
307
+ * optionally restore revoked devices. They are excluded from the active key
308
+ * arrays (via {@link applyKeyPairFields}), so the server/auth treat them as
309
+ * having no access — identical observable behavior to the previous
310
+ * delete-based revocation.
311
+ */
312
+ removeUserKeys(username: string, signingKeys: string[], encryptionKeys: string[], administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
313
+ /**
314
+ * Set or clear the label of a device's key pair, identified by its signing
315
+ * public key (§6.5). An empty/whitespace `label` clears it. No-op if no pair
316
+ * with that signing key exists on the user's grant.
317
+ */
318
+ setKeyPairLabel(username: string, signingPublicKey: string, label: string, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
319
+ /**
320
+ * Request a remote wipe of specific devices (§6.5). Targets devices by signing
321
+ * public key; the values are stored self-contained in
322
+ * `wipeRequestedForSigningKeys` so they survive key removal. This is an
323
+ * explicit, opt-in directive and is independent of revocation.
324
+ */
325
+ requestDeviceWipe(username: string, signingKeys: string[], administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
326
+ /** Cancel a previously-requested device wipe for the given signing keys (§6.5). */
327
+ cancelDeviceWipe(username: string, signingKeys: string[], administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
328
+ /**
329
+ * Read the current grant overview for a user, for prefilling an admin
330
+ * "manage user" UI: the decrypted user-details payload (requires the tenant
331
+ * default key, which the admin client holds), the active device key pairs,
332
+ * and the retained revoked device key pairs (each with its `revokedAt` and
333
+ * remote-wipe status). Devices are de-duplicated by signing key across the
334
+ * user's grant documents (most recent wins).
335
+ */
336
+ getUserGrantOverview(username: string): Promise<{
337
+ details: DirectoryUserDetails | null;
338
+ activeDevices: GrantKeyPairInfo[];
339
+ revokedDevices: Array<GrantKeyPair & {
340
+ wipeRequested: boolean;
341
+ }>;
342
+ }>;
343
+ /**
344
+ * Apply a batch of admin-signed edits to a user's grant in a single change per
345
+ * grant document (docs/accesscontrol.md §6.5). In one pass this:
346
+ * - rewrites `user_details_encrypted` (when `changes.details` is supplied),
347
+ * - recomputes the `identity_hashes` bundle + version (the per-user backfill
348
+ * trigger for grants written before the bundle existed),
349
+ * - sets per-device labels (`deviceLabels`: signing key → label; empty clears),
350
+ * - revokes devices (`revokeSigningKeys`: marks `revoked:true` + `revokedAt`,
351
+ * retaining the pair) and restores devices (`restoreSigningKeys`: clears the
352
+ * revoked flags, moving the key back to active and dropping any wipe flag),
353
+ * - and sets the remote-wipe set (`wipeSigningKeys`, authoritative when
354
+ * supplied) minus any restored keys.
355
+ *
356
+ * This is the SDK primitive behind the Haven "Manage user" dialog's batched
357
+ * Save: every staged change is committed atomically per grant document.
358
+ */
359
+ updateUserGrant(username: string, changes: {
360
+ details?: DirectoryUserDetails;
361
+ deviceLabels?: Record<string, string>;
362
+ revokeSigningKeys?: string[];
363
+ restoreSigningKeys?: string[];
364
+ wipeSigningKeys?: string[];
365
+ }, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
366
+ /**
367
+ * Predict whether the current user may perform `op` on `dbid` (§9). Pure read;
368
+ * evaluates Tier 1 + Tier 2 against the head directory state, treating the
369
+ * caller as the prospective author. `candidateDoc` supplies the content that
370
+ * `withfields` clauses are checked against.
371
+ */
372
+ canDo(op: RuleType, dbid: string, candidateDoc?: Record<string, unknown>): Promise<AccessDecision>;
373
+ /**
374
+ * Audit query (§9): would `username` have been allowed to perform `op` on
375
+ * `dbid` at trusted time `at`? Evaluates against the directory state as it was
376
+ * at `at`, so historical decisions can be explained.
377
+ */
378
+ wasAllowedAt(op: RuleType, username: string, dbid: string, at: number, candidateDoc?: Record<string, unknown>): Promise<AccessDecision>;
379
+ /**
380
+ * All ACTIVE (non-revoked) RSA encryption public keys (PEM) for `username`
381
+ * across the user's grant documents — one per active device. De-duplicated.
382
+ */
383
+ getUserEncryptionPublicKeys(username: string): Promise<string[]>;
384
+ /**
385
+ * Fingerprint of an RSA encryption public key (PEM): SHA-256 over the decoded
386
+ * SPKI body, first 8 bytes as colon-separated hex. Matches Haven's
387
+ * `getPublicKeyFingerprint`, so the device-map keys built here line up with
388
+ * what Haven UIs display. Stable for a given key value across wrap (key-holder)
389
+ * and reconcile (recipient) sides.
390
+ */
391
+ private encryptionKeyFingerprint;
392
+ /**
393
+ * Wrap every stored version of `keyId` (from the caller's KeyBag) to ALL
394
+ * active encryption devices of `username`, producing the exact `pushto`
395
+ * device-map shape used by both the distribution doc (`pushto_users_keys`) and
396
+ * the request URI (`KeyDistributionRequest.pushto[].devices`):
397
+ * `deviceEncKeyFingerprint -> { versionFingerprint -> wrappedKey(b64) }`.
398
+ * Throws if the caller does not hold the key or the recipient has no active
399
+ * encryption device.
400
+ */
401
+ wrapKeyForUserDevices(keyId: string, username: string): Promise<KeyDistributionPushRecipient>;
402
+ /**
403
+ * The canonical write-time hash for `username` (the value stored in
404
+ * `pushto_users_hashes` / `pullfrom_users_hashes`). Exposed so callers (the
405
+ * Haven dialog/session) can build `pullfrom` entries for users they are
406
+ * removing.
407
+ */
408
+ getUsernameHash(username: string): Promise<string>;
409
+ /**
410
+ * The version manifest (`{createdAt, fingerprint}`) of `keyId` from the
411
+ * caller's KeyBag. The single source of truth for the distribution's
412
+ * `keyVersions`. Empty when the key is not held.
413
+ */
414
+ getKeyVersionManifest(keyId: string): Promise<KeyVersionRef[]>;
415
+ /** Encrypt a UTF-8 string with the tenant default key, base64-encoded. */
416
+ private encryptToDefaultField;
417
+ /**
418
+ * Admin-sign and upsert the singleton `acl_keydistribution_<keyId>` document
419
+ * from a {@link KeyDistributionRequest} (built in-dialog or decoded from a
420
+ * request URI). The full desired state is written each time. Validates the
421
+ * structural invariants plus, against the directory, that every `pushto` user
422
+ * has an active grant and every active device is covered by wrapped material.
423
+ */
424
+ publishKeyDistribution(request: KeyDistributionRequest, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
425
+ /**
426
+ * Decrypt a `<field>_encrypted` JSON array of strings on a distribution doc.
427
+ * Returns null when the field is missing/unreadable (e.g. the tenant default
428
+ * key is not held).
429
+ */
430
+ private decryptUsernameArray;
431
+ /** Parse a raw distribution doc's data into a {@link KeyDistributionView}. */
432
+ private toKeyDistributionView;
433
+ /**
434
+ * List all key-distribution documents at the directory head as
435
+ * {@link KeyDistributionView}s (decrypting display fields when the tenant
436
+ * default key is held; null-tolerant otherwise).
437
+ */
438
+ listKeyDistributions(): Promise<KeyDistributionView[]>;
439
+ /** Delete the singleton distribution document for `keyId` (admin-signed). */
440
+ deleteKeyDistribution(keyId: string, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
441
+ /**
442
+ * Internal entry point for `MindooTenant.reconcileKeyDistributionsForCurrentUser`'s
443
+ * import pass ({@link KeyBagReconciler}). Unlocks this directory's OWN tenant
444
+ * session key and merges the key versions pushed to `username`. The key never
445
+ * crosses an API boundary — callers cannot supply a foreign one. Returns the
446
+ * imported/removed ids; both empty when the host is locked (no session key).
447
+ */
448
+ reconcileImportedKeysForCurrentUser(username: string): Promise<{
449
+ imported: string[];
450
+ removed: string[];
451
+ }>;
452
+ /**
453
+ * Reconcile the local KeyBag against the directory head for `username`
454
+ * (docs/accesscontrol.md §13), driven by an already-imported RSA-OAEP private
455
+ * {@link CryptoKey} (usage `["decrypt"]`). Pure function of (head, bag):
456
+ * idempotent and convergent, so an offline client catches up after one sync.
457
+ * Private: the only callers are this class's own
458
+ * {@link reconcileImportedKeysForCurrentUser}, which sources the key from the
459
+ * tenant, so no foreign key is ever applied to the bag. Per
460
+ * `acl_keydistribution_` doc:
461
+ *
462
+ * - **pullfrom** (wins): remove the whole key id from the bag (all versions),
463
+ * matching the server's key-id-grain blacklist; a no-op when the bag does
464
+ * not hold it. The KeyBag change feed triggers visibility reconciliation,
465
+ * purging the scope once the key is gone. Never touches protected ids.
466
+ * - **pushto**: unwrap own device entries, VERIFY each unwrapped version's
467
+ * fingerprint against the manifest (reject + log on mismatch), and merge the
468
+ * missing versions (never destructive). Self-healing: a locally deleted
469
+ * pushed key is re-imported here.
470
+ *
471
+ * Returns the key ids imported and removed.
472
+ */
473
+ private reconcileKeyDistributionsWithKey;
474
+ /**
475
+ * Whether `keyId` is managed by a distribution at the directory head AND the
476
+ * current/given user is in its `pushto` list (status is derived, never
477
+ * persisted in the KeyBag).
478
+ */
479
+ getManagedKeyIds(username: string): Promise<string[]>;
480
+ /**
481
+ * Admin-sign and upsert the singleton `acl_appdistribution_<appId>` document
482
+ * from an {@link AppDistributionRequest} (built in-dialog or decoded from a
483
+ * request URI). The full desired state is written each time. Validates the
484
+ * structural invariants plus, against the directory, that every `pushto` user
485
+ * has an active grant.
486
+ */
487
+ publishAppDistribution(request: AppDistributionRequest, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
488
+ /** Parse a raw app-distribution doc's data into an {@link AppDistributionView}. */
489
+ private toAppDistributionView;
490
+ /**
491
+ * List all app-distribution documents at the directory head as
492
+ * {@link AppDistributionView}s (decrypting display fields when the tenant
493
+ * default key is held; null-tolerant otherwise).
494
+ */
495
+ listAppDistributions(): Promise<AppDistributionView[]>;
496
+ /** Delete the singleton distribution document for `appId` (admin-signed). */
497
+ deleteAppDistribution(appId: string, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
498
+ /**
499
+ * Build the set of recipient-matching hashes for `username`: the user's own
500
+ * username-hash candidates plus the hash candidates of every group the user
501
+ * belongs to (resolved against the directory head). App-distribution
502
+ * `pushto`/`pullfrom` group hashes are written as the v3 hash of the
503
+ * normalized group name, which is exactly the v3 candidate of the group name,
504
+ * so a member intersects a group target here.
505
+ */
506
+ private appDistributionIdentityHashes;
507
+ /**
508
+ * The per-user app-distribution reconcile plan at the directory head
509
+ * (docs/accesscontrol.md §13): `have` is every app the user is entitled to
510
+ * (matched by `pushto` user/group and NOT by `pullfrom` — pull wins on any
511
+ * overlap), `notHave` is every other distributed app id (the user must not
512
+ * keep it). Drives the Haven install/update/remove pass after a directory
513
+ * sync. Idempotent and convergent.
514
+ */
515
+ getAppDistributionsForCurrentUser(username: string): Promise<AppDistributionReconcilePlan>;
516
+ /**
517
+ * The app ids the active/given user is entitled to receive via `pushto` at the
518
+ * directory head (status is derived, never persisted). Convenience wrapper
519
+ * over {@link getAppDistributionsForCurrentUser}.
520
+ */
521
+ getManagedAppIds(username: string): Promise<string[]>;
522
+ /**
523
+ * Admin-sign and upsert the `acl_syncsetuppolicy_<policyId>` document from a
524
+ * {@link SyncSetupPolicyRequest} (built in-dialog or decoded from a request
525
+ * URI). The full desired state is written each time. Validates the structural
526
+ * invariants plus, against the directory, that every `pushto` user has an
527
+ * active grant.
528
+ */
529
+ publishSyncSetupPolicy(request: SyncSetupPolicyRequest, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
530
+ /** Parse a raw sync-setup-policy doc's data into a {@link SyncSetupPolicyView}. */
531
+ private toSyncSetupPolicyView;
532
+ /**
533
+ * List all sync-setup-policy documents at the directory head as
534
+ * {@link SyncSetupPolicyView}s (decrypting display fields when the tenant
535
+ * default key is held; null-tolerant otherwise).
536
+ */
537
+ listSyncSetupPolicies(): Promise<SyncSetupPolicyView[]>;
538
+ /** Delete the sync-setup-policy document for `policyId` (admin-signed). */
539
+ deleteSyncSetupPolicy(policyId: string, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
540
+ /**
541
+ * The per-user sync-setup reconcile plan at the directory head: the union of
542
+ * databases the user is targeted for across all policies (matched by `pushto`
543
+ * user/group), each carrying the effective lock state. A database is locked
544
+ * when a `permanent` policy targets it AND the user is not released via
545
+ * `pullfrom`; lock wins on overlap. A user only present in `pullfrom` (never
546
+ * in `pushto`) is not targeted. Drives the Haven Sync-page seed/lock pass
547
+ * after a directory sync. Idempotent and convergent.
548
+ */
549
+ getSyncSetupForCurrentUser(username: string): Promise<SyncSetupPolicyReconcilePlan>;
550
+ /**
551
+ * The decryption key ids revoked for `username` at the directory head: every
552
+ * `acl_keydistribution_<keyId>` whose `pullfrom_users_hashes` matches one of
553
+ * the user's hash candidates (docs/accesscontrol.md §13). Protected ids (the
554
+ * tenant default / public-infos keys) are never reported. Read straight from
555
+ * the {@link keyDistributionCache} (O(cache size), no doc scan) so the sync
556
+ * server can apply the per-user blacklist on every pull/push, and the client
557
+ * can bulk-remove these ids from its KeyBag.
558
+ */
559
+ getRevokedDecryptionKeyIdsForUser(username: string): Promise<string[]>;
560
+ /**
561
+ * Signing-key variant of {@link getRevokedDecryptionKeyIdsForUser}, for the
562
+ * sync server which authenticates principals by signing public key. The tenant
563
+ * admin and any non-user (service) key are never revoked, so they get an empty
564
+ * list.
565
+ *
566
+ * Matching is done purely in hash space against the grant's precomputed,
567
+ * `$publicinfos`-readable `identity_hashes` bundle (docs/accesscontrol.md §6.5,
568
+ * §13). This is the ONLY form that works on a real sync server: the server
569
+ * holds only the `$publicinfos` key, never the tenant default key, so it can
570
+ * NEVER decrypt `user_details_encrypted` — {@link getUserBySigningPublicKey}
571
+ * returns the bare `username_hash` as `username`. Re-hashing that single hash
572
+ * (routing through {@link getRevokedDecryptionKeyIdsForUser}, the old behavior)
573
+ * produces hash-of-hash values that cannot match the multi-variant
574
+ * `pullfrom_users_hashes`, so the blacklist silently did nothing on the server.
575
+ * The precomputed bundle, written from the cleartext name at grant time, is
576
+ * server-readable and carries every variant.
577
+ */
578
+ getRevokedDecryptionKeyIdsForSigningKey(signingKey: string): Promise<string[]>;
579
+ /**
580
+ * The hash set identifying a looked-up grant, for hash-space matching against
581
+ * directory rules / blacklists (docs/accesscontrol.md §6.5). Prefers the
582
+ * precomputed `$publicinfos`-readable `identity_hashes` bundle (the v1/v2/v3
583
+ * hashes of every DN-hierarchy username variant), so it works on a sync server
584
+ * that cannot read the cleartext name. Legacy grants written before the bundle
585
+ * existed (`identityHashesV` 0/absent) carry only the single exact-match
586
+ * `username_hash`; when the cleartext name IS available (a client that holds
587
+ * the tenant key) we additionally recompute the scheme variants so matching is
588
+ * no weaker than before the bundle existed.
589
+ */
590
+ private identityHashesForLookup;
591
+ /**
592
+ * Hash-space core of the revoked-key blacklist (docs/accesscontrol.md §13):
593
+ * every `acl_keydistribution_<keyId>` whose `pullfrom_users_hashes` intersects
594
+ * `myHashes`. Protected ids (the tenant default / public-infos keys) are never
595
+ * reported. Reads straight from {@link keyDistributionCache} (O(cache size),
596
+ * no doc scan); the caller must have refreshed it first (both entry points do,
597
+ * via `updateUnifiedCache` / `getUserBySigningPublicKey`).
598
+ */
599
+ private collectRevokedKeyIdsForHashes;
600
+ /** Encode bytes as base64 (mirrors RSAEncryption's transport encoding). */
601
+ private bytesToBase64;
602
+ /** Decode a base64 string into bytes. */
603
+ private base64ToBytes;
604
+ /**
605
+ * Load the user's active grant document and apply an admin-signed edit to its
606
+ * key/wipe arrays. Shared by {@link addUserKeys}, {@link removeUserKeys},
607
+ * {@link requestDeviceWipe}, and {@link cancelDeviceWipe}.
608
+ */
609
+ private editGrantArrays;
610
+ /** Load an ACL document by its fixed id, creating it (admin-signed) if absent. */
611
+ private getOrCreateAclDoc;
612
+ /**
613
+ * Resolve the identity set for a signing key (admin, granted user, or trusted
614
+ * non-user key), shared by the server and client evaluation paths.
615
+ */
616
+ private identitySetForSigningKey;
617
+ /**
618
+ * The identity set for a key with no resolvable username (admin or a trusted
619
+ * service key): only the pseudo-tokens apply.
620
+ */
621
+ private minimalIdentitySet;
622
+ /**
623
+ * Build an identity set for access evaluation purely from a set of identity
624
+ * hashes (docs/accesscontrol.md §6.5/§7), without the cleartext username. Used
625
+ * on a $publicinfos-only sync server, which cannot read the cleartext name but
626
+ * must still match Tier 1 username/group rules in hash space. Group membership
627
+ * is resolved from the hashes (group docs store `members_hashes` and a
628
+ * `$publicinfos`-readable `groupName`), and each resolved group's name hashes
629
+ * are folded in so group-targeted rules match. Cleartext
630
+ * `username`/`usernames`/`groups` are left empty: the server defers every
631
+ * Tier 2 (placeholder) rule to clients, so they are never read here.
632
+ */
633
+ private buildIdentitySetFromHashes;
28
634
  /**
29
635
  * Normalize group name to lowercase for case-insensitive comparison.
30
636
  */
@@ -42,6 +648,15 @@ export declare class BaseMindooTenantDirectory implements MindooTenantDirectory
42
648
  * Stops on cycles with a console warning.
43
649
  */
44
650
  private resolveGroupsForUser;
651
+ /**
652
+ * Hash-space core of {@link resolveGroupsForUser}: given the set of a user's
653
+ * username-variant hashes (v1+v2 of every DN-hierarchy variant), recursively
654
+ * resolve all groups that contain the user (directly or via nested groups).
655
+ * Works purely from hashes, so the server can resolve group membership for a
656
+ * key-based reader using the precomputed `identity_hashes` bundle — no
657
+ * cleartext username required (docs/accesscontrol.md §6.5).
658
+ */
659
+ private resolveGroupsFromVariantHashes;
45
660
  /**
46
661
  * Get a user's public keys from the directory.
47
662
  * Used for authentication (signature verification) and encryption (transport encryption).
@@ -55,6 +670,36 @@ export declare class BaseMindooTenantDirectory implements MindooTenantDirectory
55
670
  details?: DirectoryUserDetails | null;
56
671
  } | null>;
57
672
  getUserBySigningPublicKey(publicKey: string): Promise<DirectoryUserLookup | null>;
673
+ /**
674
+ * Resolve the full set of signing keys relevant to authenticating a device for
675
+ * `username` (docs/accesscontrol.md §6.5):
676
+ * - `active`: keys currently granted (the device may sync normally), and
677
+ * - `wipeRequested`: keys an admin has targeted for remote wipe.
678
+ *
679
+ * The two sets are independent: a stolen device may still be active yet
680
+ * wipe-targeted, or revoked (removed from `active`) yet still wipe-targeted.
681
+ * The server uses this to let a wipe-targeted (possibly revoked) device
682
+ * authenticate just far enough to receive the wipe directive.
683
+ */
684
+ getUserSigningKeyUniverse(username: string): Promise<{
685
+ active: string[];
686
+ wipeRequested: string[];
687
+ }>;
688
+ /**
689
+ * List a user's currently-granted device key pairs (§6.5), each with its
690
+ * optional label and current remote-wipe status. Returns one entry per
691
+ * signing key (revoked keys are excluded). Used by admin UIs to pick specific
692
+ * devices to revoke or relabel. Grant documents are processed oldest→newest,
693
+ * so the most recent label/encryption key for a signing key wins.
694
+ */
695
+ getUserKeyPairs(username: string): Promise<GrantKeyPairInfo[]>;
696
+ /**
697
+ * If `signingKey` is the target of an admin-requested remote wipe (§6.5),
698
+ * return the id of the admin-signed grant document carrying the directive so
699
+ * the sync server can serve only that document to the targeted device. Returns
700
+ * null when the key is not wipe-targeted.
701
+ */
702
+ getWipeGrantDocId(signingKey: string): Promise<string | null>;
58
703
  /**
59
704
  * Check if a user has been revoked.
60
705
  * A user is considered revoked if they have no active (non-revoked) grant access documents.
@@ -63,10 +708,31 @@ export declare class BaseMindooTenantDirectory implements MindooTenantDirectory
63
708
  * @return True if the user has been revoked, false if they have active access
64
709
  */
65
710
  isUserRevoked(username: string): Promise<boolean>;
66
- requestDocHistoryPurge(dbId: string, docId: string, reason: string | undefined, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
711
+ /**
712
+ * Publish an admin-signed document-history purge request (docs/accesscontrol.md
713
+ * §13). Maps an unsigned {@link DocHistoryPurgeRequest} (built in-dialog or
714
+ * decoded from an `mdb://doc-history-purge/...` URI) to a
715
+ * {@link DocHistoryPurgeDoc} stored at the fixed id
716
+ * `acl_dochistorypurge_<requestId>` (one doc per request; append-only audit
717
+ * record). The doc shell is `$publicinfos`-readable so the sync server can read
718
+ * the cleartext `dbId` + `docIds` routing fields and execute the purge on its
719
+ * own stores; only `reason` is encrypted with the tenant `default` key.
720
+ */
721
+ publishDocHistoryPurge(request: DocHistoryPurgeRequest, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
722
+ /** Parse a raw purge-request doc's data into a {@link DocHistoryPurgeView}. */
723
+ private toDocHistoryPurgeView;
724
+ /**
725
+ * List all purge-request documents at the directory head as
726
+ * {@link DocHistoryPurgeView}s (decrypting `reason` when the tenant default
727
+ * key is held; null-tolerant otherwise).
728
+ */
729
+ listDocHistoryPurges(): Promise<DocHistoryPurgeView[]>;
730
+ /** Delete a still-pending purge request document (admin-signed). */
731
+ deleteDocHistoryPurge(requestId: string, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
67
732
  getRequestedDocHistoryPurges(): Promise<Array<{
733
+ requestId: string;
68
734
  dbId: string;
69
- docId: string;
735
+ docIds: string[];
70
736
  reason?: string;
71
737
  requestedAt: number;
72
738
  purgeRequestDocId: string;
@@ -83,18 +749,112 @@ export declare class BaseMindooTenantDirectory implements MindooTenantDirectory
83
749
  deleteGroup(groupName: string, administrationPrivateKey: EncryptedPrivateKey, administrationPrivateKeyPassword: string): Promise<void>;
84
750
  getUserNamesList(username: string): Promise<string[]>;
85
751
  /**
86
- * Compute SHA-256 hash of lowercase username for lookups.
87
- * This enables searching for users without exposing the actual username.
752
+ * Current username-hash scheme version written on new directory documents.
753
+ * v1 = unsalted `SHA-256(lower(username))` (legacy);
754
+ * v2 = tenant-salted `SHA-256(tenantId + "/" + lower(username))`;
755
+ * v3 = tenant-salted over the **NFKC-normalized** lowercase username,
756
+ * `SHA-256(tenantId + "/" + lower(NFKC(username)))`.
757
+ * v3 defends against Unicode homoglyph/normalization tricks (e.g. fullwidth
758
+ * or decomposed forms that look identical but hash differently), so an
759
+ * attacker cannot register a visually-equal variant of a privileged name and
760
+ * have it treated as a distinct identity. See docs/accesscontrol.md §6.5
761
+ * ("Username hashing"). Matching keeps v1/v2/v3 candidates so documents
762
+ * written under any prior scheme still resolve (backward compatible).
763
+ */
764
+ private static readonly USERNAME_HASH_VERSION;
765
+ /**
766
+ * Version of the identity-hash bundle (`identity_hashes`) variant-generation
767
+ * algorithm (docs/accesscontrol.md §6.5). This is a SEPARATE axis from
768
+ * {@link USERNAME_HASH_VERSION} (which versions only the hash scheme): it
769
+ * tracks how completely {@link generateUsernameVariants} enumerated the
770
+ * DN-hierarchy wildcards. Bump it whenever `generateUsernameVariants` is
771
+ * extended so readers can detect stale bundles and trigger a backfill.
772
+ */
773
+ private static readonly IDENTITY_VARIANTS_VERSION;
774
+ /**
775
+ * Compute the precomputed identity-hash bundle for a cleartext username
776
+ * (docs/accesscontrol.md §6.5): the flattened, deduped v1+v2 hashes of every
777
+ * DN-hierarchy variant from {@link generateUsernameVariants}. Stored on the
778
+ * grant doc so the server can match wildcard/group read rules in hash space
779
+ * without ever holding the cleartext name. Only username variant hashes are
780
+ * included; group membership stays dynamic (resolved at read time).
781
+ */
782
+ private computeIdentityHashes;
783
+ /**
784
+ * Legacy (v1) username hash: `SHA-256(lower(username))`, hex-encoded.
785
+ *
786
+ * Retained because pre-existing directory documents (and clients that predate
787
+ * the salted scheme) store this form, and lookups must keep matching them.
88
788
  *
89
789
  * @param username The username to hash
90
790
  * @return The hex-encoded SHA-256 hash of the lowercase username
91
791
  */
92
792
  private hashUsername;
793
+ /**
794
+ * Salted (v2) username hash: `SHA-256(tenantId + "/" + lower(username))`,
795
+ * hex-encoded. The tenant id salt prevents precomputed/rainbow-table attacks
796
+ * and cross-tenant hash correlation. New documents write this form
797
+ * (docs/accesscontrol.md §6.5).
798
+ */
799
+ private hashUsernameSalted;
800
+ /**
801
+ * NFKC-normalized salted (v3) username hash:
802
+ * `SHA-256(tenantId + "/" + lower(NFKC(username)))`, hex-encoded. NFKC
803
+ * canonicalizes Unicode so visually-equivalent or differently-encoded forms
804
+ * collapse to one hash, blocking homoglyph/normalization impersonation. New
805
+ * documents write this form. For pure-ASCII usernames it equals v2, so
806
+ * existing common cases are unaffected.
807
+ */
808
+ private hashUsernameSaltedNormalized;
809
+ /**
810
+ * The canonical hash to WRITE on new directory documents (currently v3,
811
+ * NFKC-normalized salted).
812
+ */
813
+ private hashUsernameForWrite;
814
+ /**
815
+ * All hash forms a given username could be stored as, for MATCHING/lookup.
816
+ * Returns the legacy (v1), salted (v2) and NFKC-normalized salted (v3) hashes
817
+ * so that documents written under any scheme are found. Deduplicated.
818
+ */
819
+ private usernameHashCandidates;
820
+ /** Hex-encoded SHA-256 of a UTF-8 string. */
821
+ private sha256Hex;
93
822
  private buildUserDetailsPayload;
94
823
  private encryptUserDetailsForTenant;
824
+ /**
825
+ * Decrypt the `user_details_encrypted` blob on a grant document. Uses the
826
+ * shared `_encrypted` / `_encrypted_key` field convention so the key id is
827
+ * resolved from the optional `user_details_encrypted_key` companion field,
828
+ * defaulting to the tenant default key for legacy grants that lack it.
829
+ */
95
830
  private decryptUserDetailsForTenant;
96
831
  private buildUserLookup;
97
832
  private encryptGroupMemberForTenant;
833
+ /**
834
+ * Encrypt a rule's cleartext targets (usernames + groups) with the tenant
835
+ * default key so admin UIs can display who a rule targets without reversing
836
+ * the salted hashes. Mirrors {@link encryptUserDetailsForTenant}: the blob is
837
+ * opaque to the sync server (it never holds the tenant key) but readable by
838
+ * tenant clients. Returns "" when there is nothing cleartext to store (e.g. a
839
+ * rule authored from raw hashes / pseudo-tokens only).
840
+ */
841
+ /**
842
+ * Resolve a rule's targets (explicit `users_hashes` + named `usernames` /
843
+ * `groups`) to the salted hash set evaluation matches against. Usernames are
844
+ * hashed as-is; **group names are normalized (lowercased) first** so they
845
+ * match the identity set, which hashes the normalized group names returned by
846
+ * {@link resolveGroupsForUser} (the group cache is keyed by
847
+ * {@link normalizeGroupName}). Without this, a mixed-case group target like
848
+ * "Analysts" would never intersect a member's identity (hashed as "analysts").
849
+ */
850
+ private resolveRuleTargetHashes;
851
+ private encryptRuleTargetsForTenant;
852
+ /**
853
+ * Decrypt a rule's {@link RuleTargets} display blob. Returns `null` for an
854
+ * empty/unreadable blob (e.g. a client lacking the tenant default key), in
855
+ * which case callers fall back to showing the raw `users_hashes`.
856
+ */
857
+ private decryptRuleTargetsForTenant;
98
858
  private decryptGroupMemberForTenant;
99
859
  /**
100
860
  * Convert a Uint8Array to a base64 string.