mindoodb 0.0.85 → 0.0.88

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 (319) hide show
  1. package/README-server.md +6 -2
  2. package/README.md +6 -4
  3. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
  4. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +2 -1
  5. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
  6. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +151 -12
  7. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
  8. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +340 -24
  9. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
  10. package/dist/browser/index.mjs +14 -0
  11. package/dist/cloudflare/DurableObjectSql.d.ts +56 -0
  12. package/dist/cloudflare/DurableObjectSql.d.ts.map +1 -0
  13. package/dist/cloudflare/DurableObjectSql.js +180 -0
  14. package/dist/cloudflare/DurableObjectSql.js.map +1 -0
  15. package/dist/cloudflare/MindooDatabaseObject.d.ts +86 -0
  16. package/dist/cloudflare/MindooDatabaseObject.d.ts.map +1 -0
  17. package/dist/cloudflare/MindooDatabaseObject.js +614 -0
  18. package/dist/cloudflare/MindooDatabaseObject.js.map +1 -0
  19. package/dist/cloudflare/MindooServerObject.d.ts +63 -0
  20. package/dist/cloudflare/MindooServerObject.d.ts.map +1 -0
  21. package/dist/cloudflare/MindooServerObject.js +521 -0
  22. package/dist/cloudflare/MindooServerObject.js.map +1 -0
  23. package/dist/cloudflare/WorkersCryptoAdapter.d.ts +7 -0
  24. package/dist/cloudflare/WorkersCryptoAdapter.d.ts.map +1 -0
  25. package/dist/cloudflare/WorkersCryptoAdapter.js +14 -0
  26. package/dist/cloudflare/WorkersCryptoAdapter.js.map +1 -0
  27. package/dist/cloudflare/host.d.ts +49 -0
  28. package/dist/cloudflare/host.d.ts.map +1 -0
  29. package/dist/cloudflare/host.js +104 -0
  30. package/dist/cloudflare/host.js.map +1 -0
  31. package/dist/cloudflare/http.d.ts +91 -0
  32. package/dist/cloudflare/http.d.ts.map +1 -0
  33. package/dist/cloudflare/http.js +266 -0
  34. package/dist/cloudflare/http.js.map +1 -0
  35. package/dist/cloudflare/index.d.ts +12 -0
  36. package/dist/cloudflare/index.d.ts.map +1 -0
  37. package/dist/cloudflare/index.js +20 -0
  38. package/dist/cloudflare/index.js.map +1 -0
  39. package/dist/cloudflare/storeRpc.d.ts +22 -0
  40. package/dist/cloudflare/storeRpc.d.ts.map +1 -0
  41. package/dist/cloudflare/storeRpc.js +85 -0
  42. package/dist/cloudflare/storeRpc.js.map +1 -0
  43. package/dist/cloudflare/types.d.ts +101 -0
  44. package/dist/cloudflare/types.d.ts.map +1 -0
  45. package/dist/cloudflare/types.js +10 -0
  46. package/dist/cloudflare/types.js.map +1 -0
  47. package/dist/cloudflare/worker.d.ts +25 -0
  48. package/dist/cloudflare/worker.d.ts.map +1 -0
  49. package/dist/cloudflare/worker.js +170 -0
  50. package/dist/cloudflare/worker.js.map +1 -0
  51. package/dist/core/BaseMindooDB.d.ts +95 -10
  52. package/dist/core/BaseMindooDB.d.ts.map +1 -1
  53. package/dist/core/BaseMindooDB.js +441 -98
  54. package/dist/core/BaseMindooDB.js.map +1 -1
  55. package/dist/core/BaseMindooTenant.d.ts +43 -0
  56. package/dist/core/BaseMindooTenant.d.ts.map +1 -1
  57. package/dist/core/BaseMindooTenant.js +157 -6
  58. package/dist/core/BaseMindooTenant.js.map +1 -1
  59. package/dist/core/BaseMindooTenantDirectory.d.ts +103 -0
  60. package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
  61. package/dist/core/BaseMindooTenantDirectory.js +371 -8
  62. package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
  63. package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
  64. package/dist/core/BaseMindooTenantFactory.js +2 -1
  65. package/dist/core/BaseMindooTenantFactory.js.map +1 -1
  66. package/dist/core/ChangeDocDraft.d.ts +133 -0
  67. package/dist/core/ChangeDocDraft.d.ts.map +1 -0
  68. package/dist/core/ChangeDocDraft.js +492 -0
  69. package/dist/core/ChangeDocDraft.js.map +1 -0
  70. package/dist/core/MindooDBServerAdmin.d.ts.map +1 -1
  71. package/dist/core/MindooDBServerAdmin.js +2 -1
  72. package/dist/core/MindooDBServerAdmin.js.map +1 -1
  73. package/dist/core/accesscontrol/directoryProjection.d.ts.map +1 -1
  74. package/dist/core/accesscontrol/directoryProjection.js +19 -0
  75. package/dist/core/accesscontrol/directoryProjection.js.map +1 -1
  76. package/dist/core/accesscontrol/evaluate.d.ts +14 -0
  77. package/dist/core/accesscontrol/evaluate.d.ts.map +1 -1
  78. package/dist/core/accesscontrol/evaluate.js +42 -1
  79. package/dist/core/accesscontrol/evaluate.js.map +1 -1
  80. package/dist/core/accesscontrol/types.d.ts +88 -4
  81. package/dist/core/accesscontrol/types.d.ts.map +1 -1
  82. package/dist/core/accesscontrol/types.js +114 -1
  83. package/dist/core/accesscontrol/types.js.map +1 -1
  84. package/dist/core/appendonlystores/network/AuthenticationService.d.ts +14 -0
  85. package/dist/core/appendonlystores/network/AuthenticationService.d.ts.map +1 -1
  86. package/dist/core/appendonlystores/network/AuthenticationService.js +27 -7
  87. package/dist/core/appendonlystores/network/AuthenticationService.js.map +1 -1
  88. package/dist/core/appendonlystores/network/IrohNetworkTransport.d.ts.map +1 -1
  89. package/dist/core/appendonlystores/network/IrohNetworkTransport.js +12 -0
  90. package/dist/core/appendonlystores/network/IrohNetworkTransport.js.map +1 -1
  91. package/dist/core/appendonlystores/network/IrohStreamIO.d.ts.map +1 -1
  92. package/dist/core/appendonlystores/network/IrohStreamIO.js +3 -23
  93. package/dist/core/appendonlystores/network/IrohStreamIO.js.map +1 -1
  94. package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.d.ts +6 -0
  95. package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.d.ts.map +1 -1
  96. package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.js +20 -1
  97. package/dist/core/appendonlystores/rpc/RpcContentAddressedStore.js.map +1 -1
  98. package/dist/core/appendonlystores/rpc/storeRpcCodec.d.ts.map +1 -1
  99. package/dist/core/appendonlystores/rpc/storeRpcCodec.js +3 -23
  100. package/dist/core/appendonlystores/rpc/storeRpcCodec.js.map +1 -1
  101. package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.d.ts +7 -0
  102. package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.d.ts.map +1 -1
  103. package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js +39 -30
  104. package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js.map +1 -1
  105. package/dist/core/appendonlystores/sqlite/SqliteDriver.d.ts +37 -1
  106. package/dist/core/appendonlystores/sqlite/SqliteDriver.d.ts.map +1 -1
  107. package/dist/core/appendonlystores/sqlite/SqliteDriver.js +99 -40
  108. package/dist/core/appendonlystores/sqlite/SqliteDriver.js.map +1 -1
  109. package/dist/core/appendonlystores/syncStores.d.ts.map +1 -1
  110. package/dist/core/appendonlystores/syncStores.js +25 -5
  111. package/dist/core/appendonlystores/syncStores.js.map +1 -1
  112. package/dist/core/appendonlystores/types.d.ts +7 -1
  113. package/dist/core/appendonlystores/types.d.ts.map +1 -1
  114. package/dist/core/appendonlystores/types.js +1 -0
  115. package/dist/core/appendonlystores/types.js.map +1 -1
  116. package/dist/core/builtinDbInvariants.d.ts +49 -0
  117. package/dist/core/builtinDbInvariants.d.ts.map +1 -1
  118. package/dist/core/builtinDbInvariants.js +86 -1
  119. package/dist/core/builtinDbInvariants.js.map +1 -1
  120. package/dist/core/crypto/AuthChallenge.d.ts +68 -0
  121. package/dist/core/crypto/AuthChallenge.d.ts.map +1 -0
  122. package/dist/core/crypto/AuthChallenge.js +118 -0
  123. package/dist/core/crypto/AuthChallenge.js.map +1 -0
  124. package/dist/core/crypto/DomainStatement.d.ts +5 -3
  125. package/dist/core/crypto/DomainStatement.d.ts.map +1 -1
  126. package/dist/core/crypto/DomainStatement.js +5 -3
  127. package/dist/core/crypto/DomainStatement.js.map +1 -1
  128. package/dist/core/crypto/encryptedFields.d.ts +11 -0
  129. package/dist/core/crypto/encryptedFields.d.ts.map +1 -1
  130. package/dist/core/crypto/encryptedFields.js +18 -0
  131. package/dist/core/crypto/encryptedFields.js.map +1 -1
  132. package/dist/core/expressions/evaluateExpression.d.ts +8 -0
  133. package/dist/core/expressions/evaluateExpression.d.ts.map +1 -1
  134. package/dist/core/expressions/evaluateExpression.js +29 -4
  135. package/dist/core/expressions/evaluateExpression.js.map +1 -1
  136. package/dist/core/expressions/metadata.js +4 -4
  137. package/dist/core/expressions/metadata.js.map +1 -1
  138. package/dist/core/index.d.ts +3 -3
  139. package/dist/core/index.d.ts.map +1 -1
  140. package/dist/core/index.js +17 -6
  141. package/dist/core/index.js.map +1 -1
  142. package/dist/core/query/executeQuery.d.ts.map +1 -1
  143. package/dist/core/query/executeQuery.js +5 -1
  144. package/dist/core/query/executeQuery.js.map +1 -1
  145. package/dist/core/query/executeQueryInclude.d.ts.map +1 -1
  146. package/dist/core/query/executeQueryInclude.js +2 -0
  147. package/dist/core/query/executeQueryInclude.js.map +1 -1
  148. package/dist/core/query/queryView.d.ts.map +1 -1
  149. package/dist/core/query/queryView.js +5 -1
  150. package/dist/core/query/queryView.js.map +1 -1
  151. package/dist/core/types.d.ts +204 -10
  152. package/dist/core/types.d.ts.map +1 -1
  153. package/dist/core/types.js.map +1 -1
  154. package/dist/core/uri/MindooURI.d.ts.map +1 -1
  155. package/dist/core/uri/MindooURI.js +3 -24
  156. package/dist/core/uri/MindooURI.js.map +1 -1
  157. package/dist/core/userkeys/index.d.ts +1 -0
  158. package/dist/core/userkeys/index.d.ts.map +1 -1
  159. package/dist/core/userkeys/index.js +5 -1
  160. package/dist/core/userkeys/index.js.map +1 -1
  161. package/dist/core/userkeys/sealedValues.d.ts +54 -0
  162. package/dist/core/userkeys/sealedValues.d.ts.map +1 -0
  163. package/dist/core/userkeys/sealedValues.js +67 -0
  164. package/dist/core/userkeys/sealedValues.js.map +1 -0
  165. package/dist/core/utils/base64.d.ts +17 -0
  166. package/dist/core/utils/base64.d.ts.map +1 -0
  167. package/dist/core/utils/base64.js +54 -0
  168. package/dist/core/utils/base64.js.map +1 -0
  169. package/dist/core/utils/dynamicImportInterop.d.ts +10 -0
  170. package/dist/core/utils/dynamicImportInterop.d.ts.map +1 -0
  171. package/dist/core/utils/dynamicImportInterop.js +22 -0
  172. package/dist/core/utils/dynamicImportInterop.js.map +1 -0
  173. package/dist/core/utils/idGeneration.d.ts.map +1 -1
  174. package/dist/core/utils/idGeneration.js +14 -3
  175. package/dist/core/utils/idGeneration.js.map +1 -1
  176. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -1
  177. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +7 -0
  178. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -1
  179. package/dist/node/server/FsServerDataStore.d.ts +23 -0
  180. package/dist/node/server/FsServerDataStore.d.ts.map +1 -0
  181. package/dist/node/server/FsServerDataStore.js +59 -0
  182. package/dist/node/server/FsServerDataStore.js.map +1 -0
  183. package/dist/node/server/IrohServerEndpoint.d.ts +1 -1
  184. package/dist/node/server/IrohServerEndpoint.d.ts.map +1 -1
  185. package/dist/node/server/IrohServerRpc.d.ts +5 -5
  186. package/dist/node/server/IrohServerRpc.d.ts.map +1 -1
  187. package/dist/node/server/IrohServerRpc.js +1 -1
  188. package/dist/node/server/IrohServerRpc.js.map +1 -1
  189. package/dist/node/server/MindooDBServer.d.ts +29 -150
  190. package/dist/node/server/MindooDBServer.d.ts.map +1 -1
  191. package/dist/node/server/MindooDBServer.js +160 -1573
  192. package/dist/node/server/MindooDBServer.js.map +1 -1
  193. package/dist/node/server/PurgedDocRegistry.d.ts +4 -27
  194. package/dist/node/server/PurgedDocRegistry.d.ts.map +1 -1
  195. package/dist/node/server/PurgedDocRegistry.js +6 -83
  196. package/dist/node/server/PurgedDocRegistry.js.map +1 -1
  197. package/dist/node/server/StoreFactory.d.ts +1 -1
  198. package/dist/node/server/StoreFactory.d.ts.map +1 -1
  199. package/dist/node/server/TenantManager.d.ts +16 -284
  200. package/dist/node/server/TenantManager.d.ts.map +1 -1
  201. package/dist/node/server/TenantManager.js +35 -1168
  202. package/dist/node/server/TenantManager.js.map +1 -1
  203. package/dist/node/server/add-to-network.js +1 -1
  204. package/dist/node/server/add-to-network.js.map +1 -1
  205. package/dist/node/server/config.d.ts +11 -15
  206. package/dist/node/server/config.d.ts.map +1 -1
  207. package/dist/node/server/config.js +30 -245
  208. package/dist/node/server/config.js.map +1 -1
  209. package/dist/node/server/expressAdapter.d.ts +23 -0
  210. package/dist/node/server/expressAdapter.d.ts.map +1 -0
  211. package/dist/node/server/expressAdapter.js +108 -0
  212. package/dist/node/server/expressAdapter.js.map +1 -0
  213. package/dist/node/server/nativeIrohStreamIO.d.ts +1 -1
  214. package/dist/node/server/nativeIrohStreamIO.d.ts.map +1 -1
  215. package/dist/node/server/peer/FileClusterAuditStorage.d.ts +16 -0
  216. package/dist/node/server/peer/FileClusterAuditStorage.d.ts.map +1 -0
  217. package/dist/node/server/peer/FileClusterAuditStorage.js +39 -0
  218. package/dist/node/server/peer/FileClusterAuditStorage.js.map +1 -0
  219. package/dist/node/server/peer/IrohPeerLink.d.ts +1 -1
  220. package/dist/node/server/peer/IrohPeerLink.d.ts.map +1 -1
  221. package/dist/node/server/peer/routes.d.ts +10 -44
  222. package/dist/node/server/peer/routes.d.ts.map +1 -1
  223. package/dist/node/server/peer/routes.js +33 -275
  224. package/dist/node/server/peer/routes.js.map +1 -1
  225. package/dist/node/server/resolveServerPassword.js +1 -1
  226. package/dist/node/server/resolveServerPassword.js.map +1 -1
  227. package/dist/node/server/server.js +1 -1
  228. package/dist/node/server/server.js.map +1 -1
  229. package/dist/node/server/serverinit.js +1 -1
  230. package/dist/node/server/serverinit.js.map +1 -1
  231. package/dist/server/CapabilityMatcher.d.ts +60 -0
  232. package/dist/server/CapabilityMatcher.d.ts.map +1 -0
  233. package/dist/server/CapabilityMatcher.js +138 -0
  234. package/dist/server/CapabilityMatcher.js.map +1 -0
  235. package/dist/server/DeviceDiscoveryService.d.ts +36 -0
  236. package/dist/server/DeviceDiscoveryService.d.ts.map +1 -0
  237. package/dist/server/DeviceDiscoveryService.js +164 -0
  238. package/dist/server/DeviceDiscoveryService.js.map +1 -0
  239. package/dist/server/PurgedDocRegistry.d.ts +35 -0
  240. package/dist/server/PurgedDocRegistry.d.ts.map +1 -0
  241. package/dist/server/PurgedDocRegistry.js +90 -0
  242. package/dist/server/PurgedDocRegistry.js.map +1 -0
  243. package/dist/server/ServerDataStore.d.ts +67 -0
  244. package/dist/server/ServerDataStore.d.ts.map +1 -0
  245. package/dist/server/ServerDataStore.js +88 -0
  246. package/dist/server/ServerDataStore.js.map +1 -0
  247. package/dist/server/SyncEventBus.d.ts +39 -0
  248. package/dist/server/SyncEventBus.d.ts.map +1 -0
  249. package/dist/server/SyncEventBus.js +41 -0
  250. package/dist/server/SyncEventBus.js.map +1 -0
  251. package/dist/server/SystemAdminAuth.d.ts +82 -0
  252. package/dist/server/SystemAdminAuth.d.ts.map +1 -0
  253. package/dist/server/SystemAdminAuth.js +227 -0
  254. package/dist/server/SystemAdminAuth.js.map +1 -0
  255. package/dist/server/TenantManager.d.ts +366 -0
  256. package/dist/server/TenantManager.d.ts.map +1 -0
  257. package/dist/server/TenantManager.js +1416 -0
  258. package/dist/server/TenantManager.js.map +1 -0
  259. package/dist/server/http/MindooDBServerCore.d.ts +313 -0
  260. package/dist/server/http/MindooDBServerCore.d.ts.map +1 -0
  261. package/dist/server/http/MindooDBServerCore.js +1410 -0
  262. package/dist/server/http/MindooDBServerCore.js.map +1 -0
  263. package/dist/server/http/types.d.ts +89 -0
  264. package/dist/server/http/types.d.ts.map +1 -0
  265. package/dist/server/http/types.js +29 -0
  266. package/dist/server/http/types.js.map +1 -0
  267. package/dist/server/index.d.ts +32 -0
  268. package/dist/server/index.d.ts.map +1 -0
  269. package/dist/server/index.js +76 -0
  270. package/dist/server/index.js.map +1 -0
  271. package/dist/server/peer/ClusterAuditLog.d.ts +71 -0
  272. package/dist/server/peer/ClusterAuditLog.d.ts.map +1 -0
  273. package/dist/server/peer/ClusterAuditLog.js +81 -0
  274. package/dist/server/peer/ClusterAuditLog.js.map +1 -0
  275. package/dist/server/peer/ClusterJobRunner.d.ts +43 -0
  276. package/dist/server/peer/ClusterJobRunner.d.ts.map +1 -0
  277. package/dist/server/peer/ClusterJobRunner.js +119 -0
  278. package/dist/server/peer/ClusterJobRunner.js.map +1 -0
  279. package/dist/server/peer/ClusterManager.d.ts +149 -0
  280. package/dist/server/peer/ClusterManager.d.ts.map +1 -0
  281. package/dist/server/peer/ClusterManager.js +292 -0
  282. package/dist/server/peer/ClusterManager.js.map +1 -0
  283. package/dist/server/peer/PeerAuthService.d.ts +69 -0
  284. package/dist/server/peer/PeerAuthService.d.ts.map +1 -0
  285. package/dist/server/peer/PeerAuthService.js +207 -0
  286. package/dist/server/peer/PeerAuthService.js.map +1 -0
  287. package/dist/server/peer/PeerReplicator.d.ts +303 -0
  288. package/dist/server/peer/PeerReplicator.d.ts.map +1 -0
  289. package/dist/server/peer/PeerReplicator.js +1072 -0
  290. package/dist/server/peer/PeerReplicator.js.map +1 -0
  291. package/dist/server/peer/clusterHttp.d.ts +113 -0
  292. package/dist/server/peer/clusterHttp.d.ts.map +1 -0
  293. package/dist/server/peer/clusterHttp.js +290 -0
  294. package/dist/server/peer/clusterHttp.js.map +1 -0
  295. package/dist/server/peer/types.d.ts +244 -0
  296. package/dist/server/peer/types.d.ts.map +1 -0
  297. package/dist/server/peer/types.js +189 -0
  298. package/dist/server/peer/types.js.map +1 -0
  299. package/dist/server/rateLimits.d.ts +119 -0
  300. package/dist/server/rateLimits.d.ts.map +1 -0
  301. package/dist/server/rateLimits.js +159 -0
  302. package/dist/server/rateLimits.js.map +1 -0
  303. package/dist/server/serverConfig.d.ts +37 -0
  304. package/dist/server/serverConfig.d.ts.map +1 -0
  305. package/dist/server/serverConfig.js +251 -0
  306. package/dist/server/serverConfig.js.map +1 -0
  307. package/dist/server/timestampProxy.d.ts +129 -0
  308. package/dist/server/timestampProxy.d.ts.map +1 -0
  309. package/dist/server/timestampProxy.js +361 -0
  310. package/dist/server/timestampProxy.js.map +1 -0
  311. package/dist/server/types.d.ts +267 -0
  312. package/dist/server/types.d.ts.map +1 -0
  313. package/dist/server/types.js +56 -0
  314. package/dist/server/types.js.map +1 -0
  315. package/dist/server/validation.d.ts +43 -0
  316. package/dist/server/validation.d.ts.map +1 -0
  317. package/dist/server/validation.js +104 -0
  318. package/dist/server/validation.js.map +1 -0
  319. package/package.json +9 -1
@@ -2,11 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseMindooTenantDirectory = void 0;
4
4
  const types_1 = require("./types");
5
+ const errors_1 = require("./errors");
5
6
  const timeSource_1 = require("./utils/timeSource");
6
7
  const singleFlight_1 = require("./utils/singleFlight");
7
8
  const logging_1 = require("./logging");
8
9
  const grantKeys_1 = require("./accesscontrol/grantKeys");
9
10
  const types_2 = require("./accesscontrol/types");
11
+ const builtinDbInvariants_1 = require("./builtinDbInvariants");
10
12
  const DirectoryStateNode_1 = require("./accesscontrol/DirectoryStateNode");
11
13
  const DirectoryTimeTravelIndex_1 = require("./accesscontrol/DirectoryTimeTravelIndex");
12
14
  const directoryProjection_1 = require("./accesscontrol/directoryProjection");
@@ -20,6 +22,48 @@ const MAX_UNKNOWN_KEYS_TRACKED = 1000;
20
22
  function unionStrings(existing, added) {
21
23
  return Array.from(new Set([...existing, ...added]));
22
24
  }
25
+ /**
26
+ * Grant documents by `username_hash`, maintained by the unified cache pass.
27
+ * Keeps ids only, never documents, so its size is independent of the
28
+ * document cache.
29
+ */
30
+ class GrantDocumentIndex {
31
+ constructor() {
32
+ this.idsByHash = new Map();
33
+ /** id -> hash and position in modification order (grows on every re-seen change). */
34
+ this.entries = new Map();
35
+ this.nextOrder = 0;
36
+ }
37
+ /** Latest state of a document: its `username_hash` if it is a grant, else undefined. */
38
+ record(docId, usernameHash) {
39
+ const previous = this.entries.get(docId);
40
+ if (previous) {
41
+ this.entries.delete(docId);
42
+ const ids = this.idsByHash.get(previous.hash);
43
+ ids?.delete(docId);
44
+ if (ids && ids.size === 0)
45
+ this.idsByHash.delete(previous.hash);
46
+ }
47
+ if (typeof usernameHash !== "string")
48
+ return;
49
+ this.entries.set(docId, { hash: usernameHash, order: this.nextOrder++ });
50
+ let ids = this.idsByHash.get(usernameHash);
51
+ if (!ids) {
52
+ ids = new Set();
53
+ this.idsByHash.set(usernameHash, ids);
54
+ }
55
+ ids.add(docId);
56
+ }
57
+ /** Ids of the grants carrying any of `hashes`, oldest change first. */
58
+ docIdsFor(hashes) {
59
+ const ids = [];
60
+ for (const hash of hashes) {
61
+ for (const id of this.idsByHash.get(hash) ?? [])
62
+ ids.push(id);
63
+ }
64
+ return ids.sort((a, b) => this.entries.get(a).order - this.entries.get(b).order);
65
+ }
66
+ }
23
67
  class BaseMindooTenantDirectory {
24
68
  constructor(tenant, logger) {
25
69
  this.directoryDB = null;
@@ -33,6 +77,13 @@ class BaseMindooTenantDirectory {
33
77
  // (docs/accesscontrol.md §6.5), so the set of trusted keys is recomputed as
34
78
  // the union of all grant documents' current key arrays after each cache pass.
35
79
  this.grantDocIdToSigningKeys = new Map();
80
+ // Index of grant documents: `username_hash` -> ids of the (non-deleted,
81
+ // readable) grant documents carrying it, plus each id's position in
82
+ // modification order. findGrantAccessDocuments loads only the documents
83
+ // it returns instead of re-scanning the whole directory: the token check
84
+ // runs it on every sync request, and with more grants than the document
85
+ // cache holds the scan rebuilt every grant from its changes each time.
86
+ this.grantIndex = new GrantDocumentIndex();
36
87
  // Best-effort reverse lookup cache for public signing key -> user details.
37
88
  // Entries remain available even after revocation so historical UIs can still show identity labels.
38
89
  this.userLookupCache = new Map();
@@ -141,7 +192,19 @@ class BaseMindooTenantDirectory {
141
192
  }
142
193
  return this.directoryDB;
143
194
  }
195
+ async registerUsers(users, administrationPrivateKey, administrationPrivateKeyPassword) {
196
+ await this.tenant.withDecryptedKeyReuse(async () => {
197
+ for (const user of users) {
198
+ await this.registerUserWithKeyReuse(user.userId, administrationPrivateKey, administrationPrivateKeyPassword, user.userDetails, user.label, user.options);
199
+ }
200
+ });
201
+ }
144
202
  async registerUser(userId, administrationPrivateKey, administrationPrivateKeyPassword, userDetails, label, options) {
203
+ // One registration signs several directory changes and key distributions
204
+ // with the administration key; decrypt it once instead of for each.
205
+ await this.tenant.withDecryptedKeyReuse(() => this.registerUserWithKeyReuse(userId, administrationPrivateKey, administrationPrivateKeyPassword, userDetails, label, options));
206
+ }
207
+ async registerUserWithKeyReuse(userId, administrationPrivateKey, administrationPrivateKeyPassword, userDetails, label, options) {
145
208
  this.logger.info(`Registering user: ${userId.username}`);
146
209
  // Check if user with same username (case-insensitive) already has ACTIVE
147
210
  // access. A grant whose keys were removed (revocation, §6.5) is ignored
@@ -350,22 +413,26 @@ class BaseMindooTenantDirectory {
350
413
  // identity round-trips: a cleartext name never collides with a 64-char hex
351
414
  // hash, so this is safe (docs/accesscontrol.md §6.5).
352
415
  targetHashes.add(username);
353
- const matchesTarget = (value) => typeof value === "string" && targetHashes.has(value);
354
416
  const matchingDocs = [];
355
- // Use the generator-based iteration API for cleaner code.
417
+ // The unified cache pass above indexed the grant documents by
418
+ // username_hash; only the matches are loaded, oldest first as the former
419
+ // full scan returned them.
356
420
  // No signature verification needed - DB already enforces admin-only.
357
421
  // Revocation no longer uses a separate document: it removes keys from the
358
422
  // grant document in place (docs/accesscontrol.md §6.5). A fully-revoked user
359
423
  // therefore still has a grant document here, but with empty key arrays.
360
424
  // Callers that need "active access" should inspect the key arrays (see
361
425
  // {@link isUserRevoked} / {@link getUserPublicKeys}).
362
- for await (const { doc } of directoryDB.iterateChangesSince(null)) {
363
- const data = doc.getData();
364
- if (data.form === "useroperation" &&
365
- data.type === "grantaccess" &&
366
- matchesTarget(data.username_hash)) {
426
+ for (const docId of this.grantIndex.docIdsFor(targetHashes)) {
427
+ try {
428
+ matchingDocs.push(await directoryDB.getDocument(docId));
367
429
  this.logger.debug(`Found grant access document for username: ${username}`);
368
- matchingDocs.push(doc);
430
+ }
431
+ catch (error) {
432
+ // Deleted since the cache pass: no longer a grant, as for the scan.
433
+ if (!(error instanceof errors_1.DocumentDeletedError) && !(error instanceof errors_1.DocumentNotFoundError)) {
434
+ throw error;
435
+ }
369
436
  }
370
437
  }
371
438
  this.logger.debug(`Found ${matchingDocs.length} grant access document(s) for username: ${username}`);
@@ -594,6 +661,7 @@ class BaseMindooTenantDirectory {
594
661
  // are idempotent) instead of leaving its documents half-applied.
595
662
  const fullRebuild = startCursor === null;
596
663
  const grantDocIdToSigningKeys = fullRebuild ? new Map() : this.grantDocIdToSigningKeys;
664
+ const grantIndex = fullRebuild ? new GrantDocumentIndex() : this.grantIndex;
597
665
  const userLookupCache = fullRebuild ? new Map() : this.userLookupCache;
598
666
  const wipeKeyToGrantDocId = fullRebuild ? new Map() : this.wipeKeyToGrantDocId;
599
667
  let tenantSettingsCache = fullRebuild ? null : this.tenantSettingsCache;
@@ -616,6 +684,9 @@ class BaseMindooTenantDirectory {
616
684
  // Process changes (documents are returned in order by lastModified, oldest to newest)
617
685
  for await (const { doc, cursor } of directoryDB.iterateChangesSince(startCursor)) {
618
686
  const data = doc.getData();
687
+ // Deleted and unreadable documents arrive as tombstones with empty
688
+ // data and drop out, as they never matched the former full scan.
689
+ grantIndex.record(doc.getId(), data.form === "useroperation" && data.type === "grantaccess" ? data.username_hash : undefined);
619
690
  // Process user operation documents (grant/revoke access)
620
691
  if (data.form === "useroperation") {
621
692
  // Check if this is a grant access document. A grant may carry MULTIPLE
@@ -813,6 +884,7 @@ class BaseMindooTenantDirectory {
813
884
  }
814
885
  this.trustedKeysCache = trustedKeysCache;
815
886
  this.grantDocIdToSigningKeys = grantDocIdToSigningKeys;
887
+ this.grantIndex = grantIndex;
816
888
  this.userLookupCache = userLookupCache;
817
889
  this.wipeKeyToGrantDocId = wipeKeyToGrantDocId;
818
890
  this.tenantSettingsCache = tenantSettingsCache;
@@ -1250,6 +1322,10 @@ class BaseMindooTenantDirectory {
1250
1322
  * server to gate all sync operations for a database id.
1251
1323
  */
1252
1324
  async evaluateDbAccessForSigningKey(input) {
1325
+ return (await this.evaluateDbAccessModeForSigningKey(input)).mode === "read";
1326
+ }
1327
+ /** The configured gates (§6.7 id allowlist, §6.8 read gate), without the personal-database invariant. */
1328
+ async evaluateConfiguredReadGate(input) {
1253
1329
  // Gate 1 — tenant-wide database-id allowlist (`databaseCreationPolicy`).
1254
1330
  // Orthogonal to read access: a database must clear BOTH gates.
1255
1331
  if (!(await this.isDatabaseAllowed(input.dbid, { signingKey: input.signingKey }))) {
@@ -1277,6 +1353,261 @@ class BaseMindooTenantDirectory {
1277
1353
  });
1278
1354
  return decision.allowed;
1279
1355
  }
1356
+ /**
1357
+ * The read scope of a principal in `dbid` (docs/accesscontrol.md §6.12):
1358
+ * `"all"` when it reads every document, `"own"` when the database's
1359
+ * `docReadScope` is `"own"` and the principal is neither the tenant admin nor
1360
+ * matched by a `doc_read_all` allow rule. The directory databases are never
1361
+ * scoped. Evaluated at the directory head, like the read gate.
1362
+ */
1363
+ async evaluateReadScopeForSigningKey(input) {
1364
+ if (input.dbid === types_1.DIRECTORY_DB_ID || input.dbid === types_1.USER_DIRECTORY_DB_ID)
1365
+ return "all";
1366
+ const adminKey = this.tenant.getAdministrationPublicKey();
1367
+ if (input.signingKey === adminKey)
1368
+ return "all";
1369
+ const decision = await this.evaluateAccessForSigningKey({
1370
+ op: "doc_read_all",
1371
+ dbid: input.dbid,
1372
+ signingKey: input.signingKey,
1373
+ trustedTime: Number.MAX_SAFE_INTEGER,
1374
+ });
1375
+ return decision.allowed ? "all" : "own";
1376
+ }
1377
+ /**
1378
+ * The current user's read scope in `dbid` (§6.12), the client-side
1379
+ * counterpart of {@link evaluateReadScopeForSigningKey}, e.g. to tell the
1380
+ * user that a list only shows their own documents.
1381
+ */
1382
+ async getReadScope(dbid) {
1383
+ const currentUser = await this.tenant.getCurrentUserId();
1384
+ return this.evaluateReadScopeForSigningKey({ dbid, signingKey: currentUser.userSigningPublicKey });
1385
+ }
1386
+ /**
1387
+ * Like {@link evaluateDbAccessForSigningKey}, but distinguishes the deposit
1388
+ * right (docs/accesscontrol.md §6.10): `"read"` passes the read gate (all
1389
+ * sync), `"deposit"` fails it but holds a `doc_deposit` allow (push of
1390
+ * sealed creates only), `"none"` neither. `maxRecipients` is the bound from
1391
+ * the matching deposit rule.
1392
+ */
1393
+ async evaluateDbAccessModeForSigningKey(input) {
1394
+ // Built-in personal-database invariant (§6.11): decides first, and a
1395
+ // configured policy can only take away what it grants.
1396
+ const personal = await this.evaluatePersonalDatabaseAccess(input);
1397
+ if (personal) {
1398
+ if (personal.mode === "none")
1399
+ return personal;
1400
+ if ((0, DirectoryStateNode_1.signingKeysEqual)(input.signingKey, this.tenant.getAdministrationPublicKey())) {
1401
+ return personal;
1402
+ }
1403
+ if (personal.mode === "read") {
1404
+ return (await this.evaluateConfiguredReadGate(input)) ? personal : { mode: "none", reason: "denied by the configured policy" };
1405
+ }
1406
+ if (!(await this.isDatabaseAllowed(input.dbid, { signingKey: input.signingKey })))
1407
+ return { mode: "none" };
1408
+ const configured = await this.evaluateAccessForSigningKey({
1409
+ op: "doc_deposit",
1410
+ dbid: input.dbid,
1411
+ signingKey: input.signingKey,
1412
+ trustedTime: Number.MAX_SAFE_INTEGER,
1413
+ });
1414
+ // Only a matching deny rule counts here; the built-in grant replaces the allow rule.
1415
+ return !configured.allowed && configured.matchedRuleId
1416
+ ? { mode: "none", reason: `deposit denied by rule ${configured.matchedRuleId}` }
1417
+ : personal;
1418
+ }
1419
+ if (await this.evaluateConfiguredReadGate(input)) {
1420
+ return { mode: "read" };
1421
+ }
1422
+ // The id allowlist (gate 1) applies to depositors as well.
1423
+ if (!(await this.isDatabaseAllowed(input.dbid, { signingKey: input.signingKey }))) {
1424
+ return { mode: "none" };
1425
+ }
1426
+ const decision = await this.evaluateAccessForSigningKey({
1427
+ op: "doc_deposit",
1428
+ dbid: input.dbid,
1429
+ signingKey: input.signingKey,
1430
+ trustedTime: Number.MAX_SAFE_INTEGER,
1431
+ });
1432
+ if (!decision.allowed)
1433
+ return { mode: "none" };
1434
+ const rule = await this.depositRule(decision.matchedRuleId);
1435
+ return {
1436
+ mode: "deposit",
1437
+ maxRecipients: rule?.maxRecipients ?? types_2.DEFAULT_DEPOSIT_MAX_RECIPIENTS,
1438
+ ...(rule?.docIdPrefixes?.length ? { docIdPrefixes: rule.docIdPrefixes } : {}),
1439
+ };
1440
+ }
1441
+ /**
1442
+ * The built-in rule for personal databases (§6.11), or `null` when `dbid` is
1443
+ * not one. `<prefix><grantDocId>` may be read and synced by the devices on
1444
+ * that grant and by the tenant admin; it may be deposited into by
1445
+ * identities whose grant lists the prefix in `depositPrefixes`; nobody
1446
+ * else gets anything. An unknown grant id leaves everyone but the admin out.
1447
+ */
1448
+ async evaluatePersonalDatabaseAccess(input) {
1449
+ const head = await this.getDirectoryStateHead();
1450
+ const owner = (0, builtinDbInvariants_1.personalDatabaseOwner)(input.dbid, head.defaultPolicy?.personalDatabasePrefixes ?? []);
1451
+ if (!owner)
1452
+ return null;
1453
+ if ((0, DirectoryStateNode_1.signingKeysEqual)(input.signingKey, this.tenant.getAdministrationPublicKey())) {
1454
+ return { mode: "read", reason: "tenant admin" };
1455
+ }
1456
+ const grant = await this.loadGrantDocument(owner.grantDocId);
1457
+ if (grant && (0, grantKeys_1.extractActiveKeyPairs)(grant).some((pair) => (0, DirectoryStateNode_1.signingKeysEqual)(pair.signingPublicKey, input.signingKey))) {
1458
+ return { mode: "read", reason: "a device of the owner" };
1459
+ }
1460
+ const signerGrants = await this.grantsForSigningKey(input.signingKey);
1461
+ const listed = (grant, field) => Array.isArray(grant[field]) && grant[field].includes(owner.prefix);
1462
+ if (signerGrants.some((grant) => listed(grant, "accessPrefixes"))) {
1463
+ return { mode: "read", reason: `service access to ${owner.prefix}* granted` };
1464
+ }
1465
+ const depositors = signerGrants.filter((grant) => listed(grant, "depositPrefixes"));
1466
+ if (depositors.length > 0) {
1467
+ // Document ids a deposit may use: the grant's `depositDocIdPrefixes`
1468
+ // (per database prefix, e.g. {"mmail_": ["memo_"]}), else the built-in
1469
+ // default for the prefix (mmail_: "memo_", MindooMail messages).
1470
+ const configured = depositors
1471
+ .map((grant) => grant.depositDocIdPrefixes)
1472
+ .map((byPrefix) => (byPrefix && Array.isArray(byPrefix[owner.prefix]) ? byPrefix[owner.prefix] : null))
1473
+ .find((list) => list !== null);
1474
+ const docIdPrefixes = configured
1475
+ ? configured.map(String).filter((p) => p.length > 0)
1476
+ : [...(builtinDbInvariants_1.BUILTIN_PERSONAL_DEPOSIT_DOC_ID_PREFIXES[owner.prefix] ?? [])];
1477
+ return {
1478
+ mode: "deposit",
1479
+ maxRecipients: 1,
1480
+ ...(docIdPrefixes.length ? { docIdPrefixes } : {}),
1481
+ reason: `deposit into ${owner.prefix}* granted`,
1482
+ };
1483
+ }
1484
+ // Internal mail (§6.11): members deposit MindooMail messages into each
1485
+ // other's mailbox, under the tenant's `internalMail` setting.
1486
+ if (owner.prefix === "mmail_" && grant && signerGrants.length > 0) {
1487
+ const mode = head.defaultPolicy?.internalMail ?? "members";
1488
+ let allowed = mode === "members";
1489
+ if (mode === "rules") {
1490
+ const decision = await this.evaluateAccessForSigningKey({
1491
+ op: "doc_deposit",
1492
+ dbid: input.dbid,
1493
+ signingKey: input.signingKey,
1494
+ trustedTime: Number.MAX_SAFE_INTEGER,
1495
+ });
1496
+ allowed = decision.allowed && decision.matchedRuleId !== undefined;
1497
+ }
1498
+ if (allowed) {
1499
+ const limits = head.defaultPolicy?.internalMailLimits ?? {};
1500
+ return {
1501
+ mode: "deposit",
1502
+ maxRecipients: 1,
1503
+ docIdPrefixes: [...(builtinDbInvariants_1.BUILTIN_PERSONAL_DEPOSIT_DOC_ID_PREFIXES.mmail_ ?? [])],
1504
+ internalMail: {
1505
+ maxBytesPerMail: limits.maxBytesPerMail ?? types_2.DEFAULT_INTERNAL_MAIL_LIMITS.maxBytesPerMail,
1506
+ maxMailsPerHour: limits.maxMailsPerHour ?? types_2.DEFAULT_INTERNAL_MAIL_LIMITS.maxMailsPerHour,
1507
+ },
1508
+ reason: `internal mail (${mode})`,
1509
+ };
1510
+ }
1511
+ }
1512
+ return { mode: "none", reason: grant ? "personal database of another person" : "personal database without a matching grant" };
1513
+ }
1514
+ /** A `grantaccess` document by id, or null (missing, deleted, or another kind of document). */
1515
+ async loadGrantDocument(grantDocId) {
1516
+ const directoryDB = await this.getDirectoryDB();
1517
+ try {
1518
+ const doc = await directoryDB.getDocument(grantDocId);
1519
+ if (doc.isDeleted())
1520
+ return null;
1521
+ const data = doc.getData();
1522
+ return data.form === "useroperation" && data.type === "grantaccess" ? data : null;
1523
+ }
1524
+ catch {
1525
+ return null;
1526
+ }
1527
+ }
1528
+ /** The active grants that list `signingKey` as a device. */
1529
+ async grantsForSigningKey(signingKey) {
1530
+ const lookup = await this.getUserBySigningPublicKey(signingKey);
1531
+ if (!lookup)
1532
+ return [];
1533
+ const grants = await this.findGrantAccessDocuments(lookup.username);
1534
+ return grants
1535
+ .filter((doc) => !doc.isDeleted())
1536
+ .map((doc) => doc.getData())
1537
+ .filter((data) => (0, grantKeys_1.extractActiveKeyPairs)(data).some((pair) => (0, DirectoryStateNode_1.signingKeysEqual)(pair.signingPublicKey, signingKey)));
1538
+ }
1539
+ /**
1540
+ * Let an identity deposit into personal databases with these prefixes
1541
+ * (§6.11), e.g. `["mmail_"]` for a mail gateway. Written on the person's
1542
+ * grant, admin-signed; an empty list takes the right away again.
1543
+ */
1544
+ async setGrantDepositPrefixes(username, prefixes, administrationPrivateKey, administrationPrivateKeyPassword) {
1545
+ await this.setGrantPersonalDatabaseRights(username, { depositPrefixes: prefixes }, administrationPrivateKey, administrationPrivateKeyPassword);
1546
+ }
1547
+ /**
1548
+ * The rights an identity has in other people's personal databases (§6.11):
1549
+ * `depositPrefixes` (push sealed creates only) and `accessPrefixes` (read
1550
+ * and sync, for a service such as the mail gateway serving every outbox).
1551
+ * Written on the identity's grant, admin-signed; a field left out stays as
1552
+ * it is, an empty list removes the right. `depositDocIdPrefixes` sets the
1553
+ * document ids a deposit may use per database prefix (e.g.
1554
+ * `{ mmail_: ["memo_"] }`, the built-in default); an empty list allows any id.
1555
+ */
1556
+ async setGrantPersonalDatabaseRights(username, rights, administrationPrivateKey, administrationPrivateKeyPassword) {
1557
+ for (const prefix of [...(rights.depositPrefixes ?? []), ...(rights.accessPrefixes ?? [])]) {
1558
+ if (!builtinDbInvariants_1.PERSONAL_DATABASE_PREFIX_REGEX.test(prefix)) {
1559
+ throw new Error(`depositPrefixes: "${prefix}" is not a personal-database prefix like "mmail_"`);
1560
+ }
1561
+ }
1562
+ for (const [prefix, ids] of Object.entries(rights.depositDocIdPrefixes ?? {})) {
1563
+ if (!builtinDbInvariants_1.PERSONAL_DATABASE_PREFIX_REGEX.test(prefix)) {
1564
+ throw new Error(`depositDocIdPrefixes: "${prefix}" is not a personal-database prefix like "mmail_"`);
1565
+ }
1566
+ (0, types_2.validateDocIdPrefixes)(ids, `depositDocIdPrefixes.${prefix}`);
1567
+ }
1568
+ const grants = (await this.findGrantAccessDocuments(username)).filter((doc) => !doc.isDeleted());
1569
+ if (grants.length === 0)
1570
+ throw new Error(`No grant for ${username}`);
1571
+ const directoryDB = await this.getDirectoryDB();
1572
+ const signingKeyPair = {
1573
+ publicKey: this.tenant.getAdministrationPublicKey(),
1574
+ privateKey: administrationPrivateKey,
1575
+ };
1576
+ for (const grant of grants) {
1577
+ await directoryDB.changeDoc(grant, (d) => {
1578
+ const data = d.getData();
1579
+ if (rights.depositPrefixes)
1580
+ data.depositPrefixes = [...new Set(rights.depositPrefixes)];
1581
+ if (rights.accessPrefixes)
1582
+ data.accessPrefixes = [...new Set(rights.accessPrefixes)];
1583
+ if (rights.depositDocIdPrefixes) {
1584
+ const merged = { ...(data.depositDocIdPrefixes ?? {}) };
1585
+ for (const [prefix, ids] of Object.entries(rights.depositDocIdPrefixes))
1586
+ merged[prefix] = [...new Set(ids)];
1587
+ data.depositDocIdPrefixes = merged;
1588
+ }
1589
+ }, { signingKeyPair, signingKeyPassword: administrationPrivateKeyPassword });
1590
+ }
1591
+ await this.updateUnifiedCache();
1592
+ }
1593
+ /**
1594
+ * Whether the current user holds the deposit right on `dbid` (§6.10): a
1595
+ * `doc_deposit` allow rule matches and no deny does. Says nothing about the
1596
+ * read gate; callers check {@link canReadDatabase} first.
1597
+ */
1598
+ async canDepositDatabase(dbid) {
1599
+ if (dbid === types_1.DIRECTORY_DB_ID || dbid === types_1.USER_DIRECTORY_DB_ID)
1600
+ return false;
1601
+ const currentUser = await this.tenant.getCurrentUserId();
1602
+ const access = await this.evaluateDbAccessModeForSigningKey({ dbid, signingKey: currentUser.userSigningPublicKey });
1603
+ return access.mode === "deposit";
1604
+ }
1605
+ async depositRule(ruleId) {
1606
+ if (!ruleId)
1607
+ return null;
1608
+ const head = await this.getDirectoryStateHead();
1609
+ return (head.rulesByType.get("doc_deposit") ?? []).find((r) => r.ruleId === ruleId) ?? null;
1610
+ }
1280
1611
  /**
1281
1612
  * Whether the current user may open and sync `dbid` under the database read
1282
1613
  * gate (`doc_read`, §6.6). The client-side counterpart of
@@ -1293,6 +1624,10 @@ class BaseMindooTenantDirectory {
1293
1624
  const adminKey = this.tenant.getAdministrationPublicKey();
1294
1625
  if (currentUser.userSigningPublicKey === adminKey)
1295
1626
  return true;
1627
+ // The personal-database invariant (§6.11) decides first, policy on top.
1628
+ const personal = await this.evaluatePersonalDatabaseAccess({ dbid, signingKey: currentUser.userSigningPublicKey });
1629
+ if (personal && personal.mode !== "read")
1630
+ return false;
1296
1631
  const decision = await this.canDo("doc_read", dbid);
1297
1632
  return decision.allowed;
1298
1633
  }
@@ -1311,12 +1646,30 @@ class BaseMindooTenantDirectory {
1311
1646
  (0, types_3.validateAccessPolicy)(policy);
1312
1647
  await this.writePolicyDoc(types_3.ACL_DEFAULT_POLICY_DOC_ID, policy, administrationPrivateKey, administrationPrivateKeyPassword);
1313
1648
  }
1649
+ async requireV2EntrySignatures(administrationPrivateKey, administrationPrivateKeyPassword) {
1650
+ const head = await this.getDirectoryStateHead();
1651
+ const existing = head.defaultPolicy?.requireMetadataSignatureSince;
1652
+ if (typeof existing === "number" && Number.isFinite(existing)) {
1653
+ return { since: existing, changed: false };
1654
+ }
1655
+ const since = (0, timeSource_1.semanticNow)();
1656
+ await this.setDefaultAccessPolicy(head.defaultPolicy
1657
+ ? { requireMetadataSignatureSince: since }
1658
+ : // No policy document yet: writing one is what activates access
1659
+ // control, so it carries the master switch, exactly as a new tenant
1660
+ // gets it from the factory.
1661
+ { disableAllAccessChecksAndPolicies: true, requireMetadataSignatureSince: since }, administrationPrivateKey, administrationPrivateKeyPassword);
1662
+ return { since, changed: true };
1663
+ }
1314
1664
  /**
1315
1665
  * Create or update a per-database policy override (§6.2). Fields set here
1316
1666
  * override the tenant default for `dbid`; unset fields inherit it.
1317
1667
  */
1318
1668
  async setDatabaseAccessPolicy(dbid, policy, administrationPrivateKey, administrationPrivateKeyPassword) {
1319
1669
  (0, types_3.validateAccessPolicy)(policy);
1670
+ if (policy.personalDatabasePrefixes !== undefined) {
1671
+ throw new Error("personalDatabasePrefixes is a tenant-level setting; set it on the default policy");
1672
+ }
1320
1673
  if (policy.llm !== undefined) {
1321
1674
  throw new Error("llm is a tenant-level setting; set it on the default policy");
1322
1675
  }
@@ -1368,6 +1721,8 @@ class BaseMindooTenantDirectory {
1368
1721
  groups: rule.groups ?? [],
1369
1722
  }),
1370
1723
  action: rule.action ?? "allow",
1724
+ ...(rule.maxRecipients !== undefined ? { maxRecipients: rule.maxRecipients } : {}),
1725
+ ...(rule.docIdPrefixes !== undefined ? { docIdPrefixes: [...new Set(rule.docIdPrefixes)] } : {}),
1371
1726
  };
1372
1727
  // Fail fast on malformed clauses / operators (§6.3 closed set).
1373
1728
  (0, types_3.validateAclRule)(ruleDoc);
@@ -1391,6 +1746,14 @@ class BaseMindooTenantDirectory {
1391
1746
  data.description = ruleDoc.description;
1392
1747
  if (ruleDoc.withfields !== undefined)
1393
1748
  data.withfields = ruleDoc.withfields;
1749
+ if (ruleDoc.maxRecipients !== undefined)
1750
+ data.maxRecipients = ruleDoc.maxRecipients;
1751
+ else if (data.maxRecipients !== undefined)
1752
+ delete data.maxRecipients;
1753
+ if (ruleDoc.docIdPrefixes?.length)
1754
+ data.docIdPrefixes = ruleDoc.docIdPrefixes;
1755
+ else if (data.docIdPrefixes !== undefined)
1756
+ delete data.docIdPrefixes;
1394
1757
  }, {
1395
1758
  signingKeyPair: adminSigningKeyPair,
1396
1759
  signingKeyPassword: administrationPrivateKeyPassword,