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
@@ -1,420 +1,30 @@
1
1
  "use strict";
2
2
  /**
3
- * TenantManager handles loading, caching, and registration of tenants.
4
- *
5
- * Uses a global server identity (one per server, not per tenant) for
6
- * authenticating with remote servers and opening tenant directories.
7
- *
8
- * When a tenant has a $publicinfos key (sent during publishToServer), the
9
- * manager creates a real BaseMindooTenant for directory reading. User
10
- * authentication is then validated against the admin-signed directory DB
11
- * rather than a static JSON config.
12
- *
13
- * When no $publicinfos key is available, a SimpleMindooDirectory backed
14
- * by config.json users[] is used as a fallback (useful for tests).
15
- *
16
- * A CompositeMindooDirectory wraps whichever directory source is used and
17
- * also checks the global trusted-servers list, enabling server-to-server
18
- * auth without servers being in each tenant's user directory.
3
+ * Node host of the {@link CoreTenantManager}: tenant state lives in files
4
+ * below the data directory and every tenant database is an on-disk store in
5
+ * `<dataDir>/<tenantId>/stores` (see {@link StoreFactory}).
19
6
  */
20
7
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.TenantManager = exports.SimpleMindooDirectory = void 0;
8
+ exports.TenantManager = exports.NodeServerStoreProvider = exports.SimpleMindooDirectory = void 0;
22
9
  const fs_1 = require("fs");
23
10
  const path_1 = require("path");
24
11
  const NodeCryptoAdapter_1 = require("../crypto/NodeCryptoAdapter");
25
- const AuthenticationService_1 = require("../../core/appendonlystores/network/AuthenticationService");
26
- const ServerNetworkContentAddressedStore_1 = require("../../appendonlystores/network/ServerNetworkContentAddressedStore");
27
- const PurgedDocRegistry_1 = require("./PurgedDocRegistry");
28
- const purgeDocumentHistory_1 = require("../../core/appendonlystores/purgeDocumentHistory");
29
- const Ed25519WitnessProvider_1 = require("../../core/accesscontrol/timestamp/Ed25519WitnessProvider");
30
- const BaseMindooTenantFactory_1 = require("../../core/BaseMindooTenantFactory");
31
- const RSAEncryption_1 = require("../../core/crypto/RSAEncryption");
32
- const privateKeyEncryption_1 = require("../../core/crypto/privateKeyEncryption");
33
- const KeyBag_1 = require("../../core/keys/KeyBag");
34
- const types_1 = require("../../core/types");
35
- const builtinDbInvariants_1 = require("../../core/builtinDbInvariants");
36
- const DirectoryStateNode_1 = require("../../core/accesscontrol/DirectoryStateNode");
37
- const tier1Evaluator_1 = require("../../core/accesscontrol/tier1Evaluator");
12
+ const TenantManager_1 = require("../../server/TenantManager");
13
+ const FsServerDataStore_1 = require("./FsServerDataStore");
38
14
  const StoreFactory_1 = require("./StoreFactory");
39
- // ---------------------------------------------------------------------------
40
- // StoreFactoryAdapter
41
- // ---------------------------------------------------------------------------
42
- class StoreFactoryAdapter {
43
- constructor(storeFactory) {
44
- this.storeFactory = storeFactory;
45
- }
46
- createStore(dbId, _options) {
47
- return {
48
- docStore: this.storeFactory.getStore(dbId, "docs"),
49
- attachmentStore: this.storeFactory.getStore(dbId, "attachments"),
50
- };
51
- }
52
- }
53
- // ---------------------------------------------------------------------------
54
- // SimpleMindooDirectory — config-based fallback
55
- // ---------------------------------------------------------------------------
56
- class SimpleMindooDirectory {
57
- constructor(config) {
58
- /** Canonical index: entries are identified by their signing public key. */
59
- this.usersByKey = new Map();
60
- /**
61
- * Backward-compat index by documentation-only username, populated only for
62
- * config entries that carry a `username`. Lets legacy username-based
63
- * challenges keep resolving; `username` is never required.
64
- */
65
- this.usersByUsername = new Map();
66
- this.revokedUsers = new Set();
67
- this.adminSigningPublicKey = config.adminSigningPublicKey;
68
- if (config.users) {
69
- for (const user of config.users) {
70
- // Identity is the signing key. `username` is documentation-only and
71
- // ignored for matching; index it only as a legacy convenience.
72
- this.usersByKey.set(user.signingPublicKey, user);
73
- if (typeof user.username === "string" && user.username.trim()) {
74
- this.usersByUsername.set(user.username.toLowerCase(), user);
75
- }
76
- }
77
- }
78
- }
79
- async getUserPublicKeys(username) {
80
- const normalizedUsername = username.toLowerCase();
81
- if (this.revokedUsers.has(normalizedUsername)) {
82
- return null;
83
- }
84
- const user = this.usersByUsername.get(normalizedUsername);
85
- if (!user) {
86
- return null;
87
- }
88
- return {
89
- signingPublicKey: user.signingPublicKey,
90
- encryptionPublicKey: user.encryptionPublicKey,
91
- };
92
- }
93
- async getUserBySigningPublicKey(publicKey) {
94
- const exact = this.usersByKey.get(publicKey);
95
- if (exact) {
96
- return this.toLookup(exact);
97
- }
98
- const needle = publicKey.replace(/\s+/g, "");
99
- if (!needle)
100
- return null;
101
- for (const [key, user] of this.usersByKey) {
102
- if (key.replace(/\s+/g, "") === needle) {
103
- return this.toLookup(user);
104
- }
105
- }
106
- return null;
107
- }
108
- toLookup(user) {
109
- if (typeof user.username === "string" &&
110
- this.revokedUsers.has(user.username.toLowerCase())) {
111
- return null;
112
- }
113
- return {
114
- username: typeof user.username === "string" ? user.username : "",
115
- signingPublicKey: user.signingPublicKey,
116
- encryptionPublicKey: user.encryptionPublicKey,
117
- details: null,
118
- };
119
- }
120
- async isUserRevoked(username) {
121
- return this.revokedUsers.has(username.toLowerCase());
122
- }
123
- async validatePublicSigningKey(publicKey, _opts) {
124
- if (publicKey === this.adminSigningPublicKey) {
125
- return true;
126
- }
127
- const user = this.usersByKey.get(publicKey);
128
- if (!user) {
129
- return false;
130
- }
131
- if (typeof user.username === "string" &&
132
- this.revokedUsers.has(user.username.toLowerCase())) {
133
- return false;
134
- }
135
- return true;
136
- }
137
- }
138
- exports.SimpleMindooDirectory = SimpleMindooDirectory;
139
- // ---------------------------------------------------------------------------
140
- // CompositeMindooDirectory — wraps tenant directory + trusted servers
141
- // ---------------------------------------------------------------------------
142
- /**
143
- * Wraps a per-tenant directory (real or config-based) and also checks
144
- * the global trusted-servers list. Holds a reference to the array so
145
- * runtime changes via the admin API are immediately visible.
146
- */
147
- class CompositeMindooDirectory {
148
- constructor(inner, trustedServers, adminBootstrapIdentity) {
149
- this.inner = inner;
150
- this.trustedServers = trustedServers;
151
- this.adminBootstrapIdentity = adminBootstrapIdentity;
152
- this.adminUsernameNormalized =
153
- adminBootstrapIdentity?.username.toLowerCase() ?? null;
154
- }
155
- async getUserPublicKeys(username) {
156
- const normalizedUsername = username.toLowerCase();
157
- // Bootstrap special-case: allow configured admin identity to authenticate
158
- // even before directory grantaccess docs are present on the server.
159
- if (this.adminBootstrapIdentity &&
160
- this.adminUsernameNormalized &&
161
- normalizedUsername === this.adminUsernameNormalized) {
162
- return {
163
- signingPublicKey: this.adminBootstrapIdentity.signingPublicKey,
164
- encryptionPublicKey: this.adminBootstrapIdentity.encryptionPublicKey,
165
- };
166
- }
167
- const result = await this.inner.getUserPublicKeys(username);
168
- if (result)
169
- return result;
170
- for (const server of this.trustedServers) {
171
- if (server.name.toLowerCase() === normalizedUsername) {
172
- return {
173
- signingPublicKey: server.signingPublicKey,
174
- encryptionPublicKey: server.encryptionPublicKey,
175
- };
176
- }
177
- }
178
- return null;
179
- }
180
- async isUserRevoked(username) {
181
- const normalizedUsername = username.toLowerCase();
182
- if (this.adminUsernameNormalized &&
183
- normalizedUsername === this.adminUsernameNormalized) {
184
- return false;
185
- }
186
- for (const server of this.trustedServers) {
187
- if (server.name.toLowerCase() === normalizedUsername) {
188
- return false;
189
- }
190
- }
191
- return this.inner.isUserRevoked(username);
192
- }
193
- async validatePublicSigningKey(publicKey, opts) {
194
- if (this.adminBootstrapIdentity &&
195
- (0, DirectoryStateNode_1.signingKeysEqual)(this.adminBootstrapIdentity.signingPublicKey, publicKey)) {
196
- return true;
197
- }
198
- const innerResult = await this.inner.validatePublicSigningKey(publicKey, opts);
199
- if (innerResult)
200
- return true;
201
- for (const server of this.trustedServers) {
202
- if (server.signingPublicKey === publicKey) {
203
- return true;
204
- }
205
- }
206
- return false;
207
- }
208
- // -----------------------------------------------------------------------
209
- // Advanced directory features forwarded to the inner directory. Config-based
210
- // inner directories (SimpleMindooDirectory) do not implement these, so the
211
- // composite degrades gracefully (no key-based identity, no read policy, no
212
- // wipe directives) — preserving legacy config-only behavior — while a real
213
- // BaseMindooTenantDirectory inner enables the key-based auth, read gate, and
214
- // remote-wipe paths (docs/accesscontrol.md §6.5).
215
- // -----------------------------------------------------------------------
216
- async getUserBySigningPublicKey(publicKey) {
217
- if (typeof this.inner.getUserBySigningPublicKey === "function") {
218
- const innerLookup = await this.inner.getUserBySigningPublicKey(publicKey);
219
- if (innerLookup)
220
- return innerLookup;
221
- }
222
- // Bootstrap/config fallback: the admin and trusted-server identities can be
223
- // resolved by their signing key even without a grantaccess document on this
224
- // server, mirroring getUserPublicKeys. Without this, key-based identity
225
- // resolution (and the validateToken active-key check below) would treat the
226
- // admin's own key as unknown the moment the directory has no admin grant.
227
- if (this.adminBootstrapIdentity &&
228
- (0, DirectoryStateNode_1.signingKeysEqual)(this.adminBootstrapIdentity.signingPublicKey, publicKey)) {
229
- return {
230
- username: this.adminBootstrapIdentity.username,
231
- signingPublicKey: this.adminBootstrapIdentity.signingPublicKey,
232
- encryptionPublicKey: this.adminBootstrapIdentity.encryptionPublicKey,
233
- details: null,
234
- };
235
- }
236
- for (const server of this.trustedServers) {
237
- if (server.signingPublicKey === publicKey) {
238
- return {
239
- username: server.name,
240
- signingPublicKey: server.signingPublicKey,
241
- encryptionPublicKey: server.encryptionPublicKey,
242
- details: null,
243
- };
244
- }
245
- }
246
- return null;
247
- }
248
- async getUserSigningKeyUniverse(username) {
249
- const base = typeof this.inner.getUserSigningKeyUniverse === "function"
250
- ? await this.inner.getUserSigningKeyUniverse(username)
251
- : { active: [], wipeRequested: [] };
252
- // The admin bootstrap identity, trusted servers, and config-based users are
253
- // valid principals even without a grantaccess document on this server (the
254
- // server config / bootstrap identity is the root of trust). Their signing
255
- // key must therefore count as ACTIVE so the token-validation gate in
256
- // AuthenticationService.validateToken (which requires the device key to be
257
- // in this active set) does not reject a freshly-issued admin/config token.
258
- // We reuse getUserPublicKeys, which already applies exactly those fallbacks.
259
- const active = new Set(base.active);
260
- const fallback = await this.getUserPublicKeys(username);
261
- if (fallback)
262
- active.add(fallback.signingPublicKey);
263
- return { active: Array.from(active), wipeRequested: base.wipeRequested };
264
- }
265
- async getUserKeyPairs(username) {
266
- if (typeof this.inner.getUserKeyPairs === "function") {
267
- return this.inner.getUserKeyPairs(username);
268
- }
269
- return [];
270
- }
271
- async getWipeGrantDocId(signingKey) {
272
- if (typeof this.inner.getWipeGrantDocId === "function") {
273
- return this.inner.getWipeGrantDocId(signingKey);
274
- }
275
- return null;
276
- }
277
- async getRevokedDecryptionKeyIdsForSigningKey(signingKey) {
278
- if (typeof this.inner.getRevokedDecryptionKeyIdsForSigningKey === "function") {
279
- return this.inner.getRevokedDecryptionKeyIdsForSigningKey(signingKey);
280
- }
281
- return [];
282
- }
283
- async evaluateDbAccessForSigningKey(input) {
284
- if (typeof this.inner.evaluateDbAccessForSigningKey === "function") {
285
- return this.inner.evaluateDbAccessForSigningKey(input);
286
- }
287
- // Config-based directories carry no database-open policy -> unrestricted.
288
- return true;
289
- }
290
- /**
291
- * Forwarded so the witness enforces Tier 1 rules on push
292
- * (docs/accesscontrol.md §7). `buildTier1Evaluator` only wires the check when
293
- * the directory it is given has this method; without the forward every push
294
- * was accepted, whatever the tenant's rules said. Config-based directories
295
- * have no rules and therefore no method, which keeps their legacy behavior.
296
- */
297
- get evaluateAccessForSigningKey() {
298
- const inner = this.inner;
299
- return typeof inner.evaluateAccessForSigningKey === "function"
300
- ? (input) => inner.evaluateAccessForSigningKey(input)
301
- : undefined;
302
- }
303
- /** Forwarded with {@link evaluateAccessForSigningKey}: `$author` at grant level. */
304
- get isSamePerson() {
305
- const inner = this.inner;
306
- return typeof inner.isSamePerson === "function" ? (input) => inner.isSamePerson(input) : undefined;
307
- }
308
- /**
309
- * Forwarded so server-side `userdirectory` writes can resolve the signer's
310
- * grant. Without this, `evaluateUserdirectoryInvariant` sees a missing
311
- * method, leaves `signerUsernameHash` null, and denies every non-admin
312
- * change as "signer is not a granted device" — including the founder
313
- * wrapping a second device of their own User-Key.
314
- */
315
- async resolveUsernameHashForSigningKey(signingKey, trustedTime) {
316
- if (typeof this.inner.resolveUsernameHashForSigningKey === "function") {
317
- return this.inner.resolveUsernameHashForSigningKey(signingKey, trustedTime);
318
- }
319
- return null;
320
- }
321
- }
322
- // ---------------------------------------------------------------------------
323
- // TenantManager
324
- // ---------------------------------------------------------------------------
325
- /**
326
- * TenantManager is responsible for:
327
- * - Loading the global server identity and trusted servers
328
- * - Loading tenant configurations from disk
329
- * - Caching loaded tenants
330
- * - Registering new tenants
331
- * - Managing tenant stores and authentication services
332
- * - Creating a real MindooDB tenant for directory reading (when keys available)
333
- * - Runtime management of trusted servers
334
- */
335
- class TenantManager {
336
- constructor(dataDir, serverPassword) {
337
- this.loadedTenants = new Map();
338
- this.serverIdentity = null;
339
- this.trustedServers = [];
340
- /** Peer wiring handed down to every server store; see setPeerAccessControl. */
341
- this.peerAccessControl = {};
342
- /** Per-tenant persistent registry of executed purges (docs/accesscontrol.md §13). */
343
- this.purgedDocRegistries = new Map();
344
- /** In-flight purge executions, keyed by normalized tenant id (serializes runs). */
345
- this.purgeInFlight = new Map();
15
+ var TenantManager_2 = require("../../server/TenantManager");
16
+ Object.defineProperty(exports, "SimpleMindooDirectory", { enumerable: true, get: function () { return TenantManager_2.SimpleMindooDirectory; } });
17
+ /** The on-disk stores of every tenant below `<dataDir>/<tenantId>/stores`. */
18
+ class NodeServerStoreProvider {
19
+ constructor(dataDir, dataStore) {
346
20
  this.dataDir = dataDir;
347
- this.serverPassword = serverPassword;
348
- this.cryptoAdapter = new NodeCryptoAdapter_1.NodeCryptoAdapter();
349
- if (!(0, fs_1.existsSync)(dataDir)) {
350
- (0, fs_1.mkdirSync)(dataDir, { recursive: true });
351
- console.log(`[TenantManager] Created data directory: ${dataDir}`);
352
- }
353
- // Load global server identity
354
- const identityPath = (0, path_1.join)(dataDir, "server.identity.json");
355
- if ((0, fs_1.existsSync)(identityPath)) {
356
- this.serverIdentity = JSON.parse((0, fs_1.readFileSync)(identityPath, "utf-8"));
357
- console.log(`[TenantManager] Loaded server identity: ${this.serverIdentity.username}`);
358
- }
359
- else {
360
- console.log(`[TenantManager] No server.identity.json found (run "npm run init" to create one)`);
361
- }
362
- // Load trusted servers
363
- const trustedPath = (0, path_1.join)(dataDir, "trusted-servers.json");
364
- if ((0, fs_1.existsSync)(trustedPath)) {
365
- this.trustedServers = JSON.parse((0, fs_1.readFileSync)(trustedPath, "utf-8"));
366
- console.log(`[TenantManager] Loaded ${this.trustedServers.length} trusted server(s)`);
367
- }
21
+ this.dataStore = dataStore;
368
22
  }
369
- // =======================================================================
370
- // Server identity
371
- // =======================================================================
372
- getServerIdentity() {
373
- return this.serverIdentity;
23
+ createTenantStores(tenantId, config) {
24
+ return new StoreFactory_1.StoreFactory(tenantId, config, this.dataDir);
374
25
  }
375
- getServerPublicInfo() {
376
- if (!this.serverIdentity)
377
- return null;
378
- return {
379
- name: this.serverIdentity.username,
380
- signingPublicKey: this.serverIdentity.userSigningKeyPair
381
- .publicKey,
382
- encryptionPublicKey: this.serverIdentity.userEncryptionKeyPair
383
- .publicKey,
384
- };
385
- }
386
- getDataDir() {
387
- return this.dataDir;
388
- }
389
- /**
390
- * Teach every server store how to recognise a trusted peer.
391
- *
392
- * `MindooDBServer` calls this once at startup. Two hooks, both required for
393
- * peer replication to be correct:
394
- *
395
- * - `peerTokenResolver` lets a peer's cluster-level JWT through the tenant
396
- * sync routes. A peer holds no per-tenant grant, so without it the mirror
397
- * would need a second replication protocol; with it the peer reuses the
398
- * client one and the store skips the per-user gates that do not apply to a
399
- * full replica.
400
- * - `trustedWitnessResolver` decides whose witness receipts may be preserved
401
- * instead of re-stamped (convergence invariant 3). Without it a replicated
402
- * entry would get a fresh `receivedAt` on every hop and "trusted time"
403
- * would mean "time it reached the last server".
404
- *
405
- * Must be called before the first {@link getServerStore}, since stores are
406
- * cached per tenant and database.
407
- */
408
- setPeerAccessControl(options) {
409
- this.peerAccessControl = options;
410
- }
411
- /**
412
- * Database ids this server holds for a tenant, read from
413
- * `{dataDir}/{tenantId}/stores`. Used by peer replication to mirror every
414
- * database rather than a configured subset.
415
- */
416
26
  listDatabases(tenantId) {
417
- const storesDir = (0, path_1.join)(this.resolveTenantDir(tenantId.toLowerCase()), "stores");
27
+ const storesDir = this.dataStore.resolvePath((0, path_1.join)(tenantId, "stores"));
418
28
  if (!(0, fs_1.existsSync)(storesDir)) {
419
29
  return [];
420
30
  }
@@ -426,774 +36,31 @@ class TenantManager {
426
36
  return [];
427
37
  }
428
38
  }
429
- /**
430
- * The server's own RSA private key, imported for decryption.
431
- *
432
- * Peer replication needs it because the remote wraps transported entries to
433
- * this server's `encryptionPublicKey` (the one in its `trusted-servers.json`),
434
- * exactly as it would wrap them for a client.
435
- */
436
- async getServerEncryptionPrivateKey() {
437
- if (this.serverEncryptionPrivateKey)
438
- return this.serverEncryptionPrivateKey;
439
- if (!this.serverIdentity || !this.serverPassword)
440
- return undefined;
441
- const subtle = this.cryptoAdapter.getSubtle();
442
- const keyBuffer = await (0, privateKeyEncryption_1.decryptPrivateKey)(this.cryptoAdapter, this.serverIdentity.userEncryptionKeyPair
443
- .privateKey, this.serverPassword, "encryption");
444
- this.serverEncryptionPrivateKey = await subtle.importKey("pkcs8", keyBuffer, { name: "RSA-OAEP", hash: "SHA-256" }, false, ["decrypt"]);
445
- return this.serverEncryptionPrivateKey;
446
- }
447
- /**
448
- * Build (and cache) the server's witness identity used to stamp receipts on
449
- * accepted entries (docs/accesscontrol.md §5.3). Returns undefined when the
450
- * server identity or its unlock password is unavailable, in which case the
451
- * server runs in pre-access-control mode (no stamping, no Tier 1 advertised).
452
- */
453
- async getWitnessSigner() {
454
- if (this.witnessSigner)
455
- return this.witnessSigner;
456
- if (!this.serverIdentity || !this.serverPassword)
457
- return undefined;
458
- const subtle = this.cryptoAdapter.getSubtle();
459
- const signingKeyBuffer = await (0, privateKeyEncryption_1.decryptPrivateKey)(this.cryptoAdapter, this.serverIdentity.userSigningKeyPair.privateKey, this.serverPassword, "signing");
460
- const signingPrivateKey = await subtle.importKey("pkcs8", signingKeyBuffer, { name: "Ed25519" }, false, ["sign"]);
461
- this.witnessSigner = {
462
- publicKeyPem: this.serverIdentity.userSigningKeyPair.publicKey,
463
- signingPrivateKey,
464
- subtle,
465
- };
466
- return this.witnessSigner;
467
- }
468
- /**
469
- * Build the trusted-time provider that stamps receipts on accepted entries
470
- * (docs/accesscontrol.md §5.3, §13). v1 wraps the server's Ed25519 witness
471
- * identity; returns undefined when no witness signer is available, leaving the
472
- * server in pre-access-control mode.
473
- */
474
- async getTimestampProvider() {
475
- const signer = await this.getWitnessSigner();
476
- if (!signer)
477
- return undefined;
478
- return new Ed25519WitnessProvider_1.Ed25519WitnessProvider({ signer, subtle: signer.subtle });
479
- }
480
- // =======================================================================
481
- // Trusted server management
482
- // =======================================================================
483
- listTrustedServers() {
484
- return [...this.trustedServers];
485
- }
486
- addTrustedServer(server) {
487
- const existing = this.trustedServers.find((s) => s.name.toLowerCase() === server.name.toLowerCase());
488
- if (existing) {
489
- throw new Error(`Trusted server "${server.name}" already exists`);
490
- }
491
- this.trustedServers.push(server);
492
- this.persistTrustedServers();
493
- console.log(`[TenantManager] Added trusted server: ${server.name}`);
494
- }
495
- /**
496
- * Add or replace a trusted server by name.
497
- *
498
- * Peer CRUD needs an upsert: `addTrustedServer` throws on an existing name,
499
- * which is right for `add-to-network` (a silent overwrite of a peer's keys
500
- * would be a trust change disguised as a config edit) but wrong for the admin
501
- * API, where changing a peer's url or role is the common case.
502
- */
503
- saveTrustedServer(server) {
504
- const idx = this.trustedServers.findIndex((s) => s.name.toLowerCase() === server.name.toLowerCase());
505
- if (idx === -1) {
506
- this.trustedServers.push(server);
507
- }
508
- else {
509
- this.trustedServers[idx] = server;
510
- }
511
- this.persistTrustedServers();
512
- console.log(`[TenantManager] Saved trusted server: ${server.name}`);
513
- }
514
- removeTrustedServer(name) {
515
- const idx = this.trustedServers.findIndex((s) => s.name.toLowerCase() === name.toLowerCase());
516
- if (idx === -1)
517
- return false;
518
- this.trustedServers.splice(idx, 1);
519
- this.persistTrustedServers();
520
- console.log(`[TenantManager] Removed trusted server: ${name}`);
521
- return true;
522
- }
523
- persistTrustedServers() {
524
- const filePath = (0, path_1.join)(this.dataDir, "trusted-servers.json");
525
- (0, fs_1.writeFileSync)(filePath, JSON.stringify(this.trustedServers, null, 2), "utf-8");
526
- }
527
- getServerKeyBagPath() {
528
- return (0, path_1.join)(this.dataDir, "server.keybag");
529
- }
530
- async loadServerKeyBag() {
531
- if (!this.serverIdentity || !this.serverPassword) {
532
- throw new Error("Server identity and server password are required to manage tenant $publicinfos keys");
533
- }
534
- const keyBag = await this.createServerKeyBag(this.serverIdentity, this.serverPassword);
535
- const keyBagPath = this.getServerKeyBagPath();
536
- if ((0, fs_1.existsSync)(keyBagPath)) {
537
- const data = (0, fs_1.readFileSync)(keyBagPath);
538
- await keyBag.load(new Uint8Array(data));
539
- }
540
- return keyBag;
541
- }
542
- async createServerKeyBag(serverUser, serverPassword) {
543
- const wrappingKey = await KeyBag_1.KeyBag.deriveWrappingKey(serverUser.userEncryptionKeyPair.privateKey, serverPassword, this.cryptoAdapter);
544
- return new KeyBag_1.KeyBag({
545
- wrappingKey,
546
- cryptoAdapter: this.cryptoAdapter,
547
- });
548
- }
549
- async saveServerKeyBag(keyBag) {
550
- const data = await keyBag.save();
551
- (0, fs_1.writeFileSync)(this.getServerKeyBagPath(), new DataView(data.buffer, data.byteOffset, data.byteLength));
552
- }
553
- bytesToBase64(bytes) {
554
- return Buffer.from(bytes).toString("base64");
555
- }
556
- mergeUniqueKeys(keys) {
557
- const seen = new Set();
558
- const unique = [];
559
- for (const key of keys) {
560
- const signature = this.bytesToBase64(key);
561
- if (seen.has(signature)) {
562
- continue;
563
- }
564
- seen.add(signature);
565
- unique.push(key);
566
- }
567
- return unique;
568
- }
569
- async computePublicInfosFingerprint(key) {
570
- const digest = await this.cryptoAdapter
571
- .getSubtle()
572
- .digest("SHA-256", key);
573
- return Array.from(new Uint8Array(digest).slice(0, 8))
574
- .map((byte) => byte.toString(16).padStart(2, "0"))
575
- .join(":");
576
- }
577
- resolveTenantDir(tenantId) {
578
- const baseDir = (0, path_1.resolve)(this.dataDir);
579
- const tenantDir = (0, path_1.resolve)(baseDir, tenantId);
580
- if (tenantDir !== baseDir && !tenantDir.startsWith(`${baseDir}${path_1.sep}`)) {
581
- throw new Error(`Resolved tenant path escapes data directory for tenantId "${tenantId}"`);
582
- }
583
- return tenantDir;
584
- }
585
- resolveTenantConfigPath(tenantId) {
586
- return (0, path_1.join)(this.resolveTenantDir(tenantId), "config.json");
587
- }
588
- async decryptEncryptedPublicInfosKey(base64Payload) {
589
- if (!this.serverIdentity) {
590
- throw new Error("Server identity not initialized; cannot decrypt encrypted $publicinfos payload");
591
- }
592
- if (!this.serverPassword) {
593
- throw new Error("Server password not configured; cannot decrypt encrypted $publicinfos payload");
594
- }
595
- const privateKeyBuffer = await (0, privateKeyEncryption_1.decryptPrivateKey)(this.cryptoAdapter, this.serverIdentity.userEncryptionKeyPair
596
- .privateKey, this.serverPassword, "encryption");
597
- const privateKey = await this.cryptoAdapter.getSubtle().importKey("pkcs8", privateKeyBuffer, {
598
- name: "RSA-OAEP",
599
- hash: "SHA-256",
600
- }, false, ["decrypt"]);
601
- const decrypted = await new RSAEncryption_1.RSAEncryption(this.cryptoAdapter).decrypt(new Uint8Array(Buffer.from(base64Payload, "base64")), privateKey);
602
- return new Uint8Array(decrypted);
603
- }
604
- async resolveIncomingPublicInfosKey(request) {
605
- if (request.encryptedPublicInfosKey) {
606
- return this.decryptEncryptedPublicInfosKey(request.encryptedPublicInfosKey);
607
- }
608
- if (request.publicInfosKey) {
609
- return new Uint8Array(Buffer.from(request.publicInfosKey, "base64"));
610
- }
611
- throw new Error("Tenant registration requires encryptedPublicInfosKey or publicInfosKey");
612
- }
613
- async getStoredPublicInfosKeys(tenantId) {
614
- const keys = [];
615
- if (this.serverIdentity &&
616
- this.serverPassword &&
617
- (0, fs_1.existsSync)(this.getServerKeyBagPath())) {
618
- const keyBag = await this.loadServerKeyBag();
619
- keys.push(...(await keyBag.getAllKeys("doc", tenantId, types_1.PUBLIC_INFOS_KEY_ID)));
620
- }
621
- return this.mergeUniqueKeys(keys);
622
- }
623
- async persistTenantPublicInfosKey(tenantId, key) {
624
- const keyBag = await this.loadServerKeyBag();
625
- const existingKeys = await keyBag.getAllKeys("doc", tenantId, types_1.PUBLIC_INFOS_KEY_ID);
626
- const incomingSignature = this.bytesToBase64(key);
627
- const alreadyStored = existingKeys.some((entry) => this.bytesToBase64(entry) === incomingSignature);
628
- if (!alreadyStored) {
629
- await keyBag.set("doc", tenantId, types_1.PUBLIC_INFOS_KEY_ID, key, Date.now());
630
- await this.saveServerKeyBag(keyBag);
631
- }
632
- }
633
- /**
634
- * All stored `$publicinfos` AES key versions for a tenant from `server.keybag`.
635
- * Empty when the tenant was never published or the server keybag is locked.
636
- */
637
- async getPublicInfosKeysForTenant(tenantId) {
638
- return this.getStoredPublicInfosKeys(tenantId.toLowerCase());
639
- }
640
- async listTenantPublicInfosFingerprints(tenantId) {
641
- const normalizedId = tenantId.toLowerCase();
642
- const configPath = this.resolveTenantConfigPath(normalizedId);
643
- if (!(0, fs_1.existsSync)(configPath)) {
644
- throw new Error(`Tenant ${normalizedId} not found`);
645
- }
646
- const fingerprints = await Promise.all((await this.getStoredPublicInfosKeys(normalizedId)).map((key) => this.computePublicInfosFingerprint(key)));
647
- return [...new Set(fingerprints)].sort();
648
- }
649
- // =======================================================================
650
- // Tenant management
651
- // =======================================================================
652
- async registerTenant(request) {
653
- const tenantId = request.tenantId.toLowerCase();
654
- const tenantDir = this.resolveTenantDir(tenantId);
655
- const configPath = this.resolveTenantConfigPath(tenantId);
656
- if (!this.serverIdentity || !this.serverPassword) {
657
- throw new Error("Tenant registration requires an unlocked server identity to store $publicinfos keys");
658
- }
659
- const publicInfosKey = await this.resolveIncomingPublicInfosKey(request);
660
- if ((0, fs_1.existsSync)(configPath)) {
661
- const existingConfig = JSON.parse((0, fs_1.readFileSync)(configPath, "utf-8"));
662
- const incomingSignature = this.bytesToBase64(publicInfosKey);
663
- const existingKeys = await this.getStoredPublicInfosKeys(tenantId);
664
- const hasMatch = existingKeys.some((key) => this.bytesToBase64(key) === incomingSignature);
665
- if (hasMatch) {
666
- console.log(`[TenantManager] Tenant ${tenantId} already registered with matching $publicinfos key`);
667
- return {
668
- context: { tenantId, config: existingConfig },
669
- created: false,
670
- };
671
- }
672
- throw new Error(`Tenant ${tenantId} already exists with different $publicinfos key`);
673
- }
674
- (0, fs_1.mkdirSync)(tenantDir, { recursive: true });
675
- const config = {
676
- adminUsername: request.adminUsername,
677
- adminSigningPublicKey: request.adminSigningPublicKey,
678
- adminEncryptionPublicKey: request.adminEncryptionPublicKey,
679
- defaultStoreType: request.defaultStoreType || "file",
680
- users: [],
681
- };
682
- try {
683
- (0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2), "utf-8");
684
- await this.persistTenantPublicInfosKey(tenantId, publicInfosKey);
685
- }
686
- catch (error) {
687
- (0, fs_1.rmSync)(tenantDir, { recursive: true, force: true });
688
- throw error;
689
- }
690
- console.log(`[TenantManager] Registered tenant: ${tenantId} ($publicinfos stored in server keybag)`);
691
- return {
692
- context: { tenantId, config },
693
- created: true,
694
- };
695
- }
696
- async getTenant(tenantId) {
697
- const normalizedId = tenantId.toLowerCase();
698
- const cached = this.loadedTenants.get(normalizedId);
699
- if (cached) {
700
- return cached;
701
- }
702
- const loaded = await this.loadTenant(normalizedId);
703
- this.loadedTenants.set(normalizedId, loaded);
704
- return loaded;
705
- }
706
- tenantExists(tenantId) {
707
- const normalizedId = tenantId.toLowerCase();
708
- const configPath = this.resolveTenantConfigPath(normalizedId);
709
- return (0, fs_1.existsSync)(configPath);
710
- }
711
- /**
712
- * Read the tenant's registered admin signing key without fully loading the
713
- * tenant. Returns `null` if the tenant is missing or the config is unreadable.
714
- */
715
- getTenantAdminSigningPublicKey(tenantId) {
716
- try {
717
- const normalizedId = tenantId.toLowerCase();
718
- const cached = this.loadedTenants.get(normalizedId);
719
- if (cached?.context.config.adminSigningPublicKey) {
720
- return cached.context.config.adminSigningPublicKey;
721
- }
722
- const configPath = this.resolveTenantConfigPath(normalizedId);
723
- if (!(0, fs_1.existsSync)(configPath)) {
724
- return null;
725
- }
726
- const config = JSON.parse((0, fs_1.readFileSync)(configPath, "utf-8"));
727
- return typeof config.adminSigningPublicKey === "string" &&
728
- config.adminSigningPublicKey.length > 0
729
- ? config.adminSigningPublicKey
730
- : null;
731
- }
732
- catch {
733
- return null;
734
- }
735
- }
736
- /**
737
- * True when `publicsignkey` is the registered tenant-admin signing key for
738
- * `tenantId` (PEM whitespace-insensitive).
739
- */
740
- isTenantAdminSigningKeyFor(tenantId, publicsignkey) {
741
- const adminKey = this.getTenantAdminSigningPublicKey(tenantId);
742
- return adminKey !== null && (0, DirectoryStateNode_1.signingKeysEqual)(adminKey, publicsignkey);
743
- }
744
- /**
745
- * True when `publicsignkey` is the registered admin signing key of any
746
- * tenant on this server. Used to issue a system JWT so that admin can
747
- * DELETE their own tenant; other `/system/*` routes stay capability-gated.
748
- */
749
- isRegisteredTenantAdminSigningKey(publicsignkey) {
750
- for (const tenantId of this.listTenants()) {
751
- if (this.isTenantAdminSigningKeyFor(tenantId, publicsignkey)) {
752
- return true;
753
- }
754
- }
755
- return false;
756
- }
757
- listTenants() {
758
- if (!(0, fs_1.existsSync)(this.dataDir)) {
759
- return [];
760
- }
761
- const entries = (0, fs_1.readdirSync)(this.dataDir);
762
- const tenants = [];
763
- for (const entry of entries) {
764
- const entryPath = (0, path_1.join)(this.dataDir, entry);
765
- const configPath = (0, path_1.join)(entryPath, "config.json");
766
- if ((0, fs_1.statSync)(entryPath).isDirectory() && (0, fs_1.existsSync)(configPath)) {
767
- tenants.push(entry);
768
- }
769
- }
770
- return tenants.sort();
771
- }
772
- /**
773
- * Update operator-owned fields of an existing tenant configuration.
774
- * Only the fields present in `updates` are overwritten.
775
- */
776
- updateTenantConfig(tenantId, updates) {
777
- const normalizedId = tenantId.toLowerCase();
778
- const configPath = this.resolveTenantConfigPath(normalizedId);
779
- if (!(0, fs_1.existsSync)(configPath)) {
780
- throw new Error(`Tenant ${normalizedId} not found`);
781
- }
782
- const config = JSON.parse((0, fs_1.readFileSync)(configPath, "utf-8"));
783
- if (updates.defaultStoreType !== undefined) {
784
- config.defaultStoreType = updates.defaultStoreType;
785
- }
786
- (0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2), "utf-8");
787
- this.loadedTenants.delete(normalizedId);
788
- console.log(`[TenantManager] Updated tenant config: ${normalizedId}`);
789
- }
790
- async removeTenant(tenantId) {
791
- const normalizedId = tenantId.toLowerCase();
792
- const tenantDir = this.resolveTenantDir(normalizedId);
793
- if (!(0, fs_1.existsSync)(tenantDir)) {
794
- throw new Error(`Tenant ${normalizedId} does not exist`);
795
- }
796
- this.loadedTenants.delete(normalizedId);
797
- if (this.serverIdentity &&
798
- this.serverPassword &&
799
- (0, fs_1.existsSync)(this.getServerKeyBagPath())) {
800
- const keyBag = await this.loadServerKeyBag();
801
- await keyBag.deleteKey("doc", normalizedId, types_1.PUBLIC_INFOS_KEY_ID);
802
- await this.saveServerKeyBag(keyBag);
803
- }
804
- (0, fs_1.rmSync)(tenantDir, { recursive: true, force: true });
805
- console.log(`[TenantManager] Removed tenant: ${normalizedId}`);
806
- }
807
- // =======================================================================
808
- // Server stores
809
- // =======================================================================
810
- async getServerStore(tenantId, dbId, storeKind) {
811
- const tenant = await this.getTenant(tenantId);
812
- const cacheKey = `${dbId}:${storeKind}`;
813
- const cached = tenant.serverStores.get(cacheKey);
814
- if (cached) {
815
- return cached;
816
- }
817
- const localStore = tenant.storeFactory.getStore(dbId, storeKind);
818
- // Access-control v1 wiring (docs/accesscontrol.md §5–§7). The witness signer
819
- // lets the server stamp receipts on accepted entries; the Tier 1 evaluator
820
- // enforces identity-tier rules at push time. Both are only active when the
821
- // server identity is unlocked and the directory supports evaluation, so
822
- // pre-access-control deployments keep their existing behavior.
823
- const timestampProvider = await this.getTimestampProvider();
824
- const directory = tenant.directory;
825
- const tier1Evaluator = this.buildTier1Evaluator(directory, localStore);
826
- const wipeGrantDocIdResolver = this.buildWipeGrantDocIdResolver(directory);
827
- // Database-open gate (directory-restricted policy). Never applied to the
828
- // directory store itself, which must always sync so the policy can be read.
829
- const dbAccessEvaluator = dbId === "directory" ? undefined : this.buildDbAccessEvaluator(directory);
830
- // Per-user revoked-key blacklist (§13). Never applied to the directory store
831
- // itself (its policy, which defines the blacklist, must always sync).
832
- const revokedKeyResolver = dbId === "directory"
833
- ? undefined
834
- : this.buildRevokedKeyResolver(directory);
835
- // Purged-document denylist (§13). Never applied to the directory store (purge
836
- // requests themselves live there and must always sync).
837
- const purgedDocResolver = dbId === "directory"
838
- ? undefined
839
- : () => this.getPurgedDocRegistry(tenantId).getPurgedDocIds(dbId);
840
- const builtinWriteContext = dbId === types_1.USER_DIRECTORY_DB_ID
841
- ? this.buildUserdirectoryWriteContext(tenant, localStore)
842
- : undefined;
843
- const serverStore = new ServerNetworkContentAddressedStore_1.ServerNetworkContentAddressedStore(localStore, directory, tenant.authService, this.cryptoAdapter, undefined, {
844
- timestampProvider,
845
- witnessDbid: dbId,
846
- tier1Evaluator,
847
- wipeGrantDocIdResolver,
848
- dbAccessEvaluator,
849
- revokedKeyResolver,
850
- purgedDocResolver,
851
- builtinWriteContext,
852
- ...this.peerAccessControl,
853
- });
854
- tenant.serverStores.set(cacheKey, serverStore);
855
- console.log(`[TenantManager] Created server store for ${tenantId}/${dbId}/${storeKind}`);
856
- return serverStore;
857
- }
858
- /**
859
- * Build a Tier 1 evaluator closure for a server store, or undefined when the
860
- * directory cannot evaluate access (e.g. config-based directory without the
861
- * access-control state chain). The closure resolves `$author` for non-create
862
- * ops by reading the document's `doc_create` entry creator key from the local
863
- * store (metadata only, no decryption) and comparing it to the change author.
864
- */
865
- /**
866
- * The witness's Tier 1 gate. The evaluation itself is shared with the peer
867
- * listener, which has to run it for entries that never reach a witness at
868
- * all — see `core/accesscontrol/tier1Evaluator.ts`.
869
- */
870
- buildTier1Evaluator(directory, localStore) {
871
- return (0, tier1Evaluator_1.buildTier1Evaluator)(directory, localStore);
872
- }
873
- /**
874
- * Builtin `userdirectory` write invariant: admin may create/delete, only the
875
- * owning person may change. `username_hash` is read from the decrypted
876
- * `doc_create` payload (`$publicinfos`). Personal documents are sealed to
877
- * their owner and therefore undecryptable here, so they are owned by whoever
878
- * signed their `doc_create` — `resolveCreatorSigningKey` answers that from
879
- * the entry chain alone.
880
- */
881
- buildUserdirectoryWriteContext(tenant, localStore) {
882
- const adminPublicKey = tenant.context.config.adminSigningPublicKey;
883
- const mindooTenant = tenant.mindooTenant;
884
- return {
885
- adminPublicKey,
886
- resolveCreatorSigningKey: async (entry) => {
887
- const createEntry = await this.findCreateEntry(localStore, entry);
888
- return createEntry?.createdByPublicKey ?? null;
889
- },
890
- resolveDocumentUsernameHash: mindooTenant
891
- ? async (entry) => {
892
- const createEntry = await this.findCreateEntry(localStore, entry);
893
- if (!createEntry)
894
- return null;
895
- try {
896
- const plaintext = await mindooTenant.decryptPayload(createEntry.encryptedData, createEntry.decryptionKeyId);
897
- return (0, builtinDbInvariants_1.usernameHashFromCreateChangeBytes)(plaintext);
898
- }
899
- catch {
900
- return null;
901
- }
902
- }
903
- : undefined,
904
- };
905
- }
906
- async findCreateEntry(localStore, entry) {
907
- if (entry.entryType === "doc_create") {
908
- return entry;
909
- }
910
- try {
911
- const metas = await localStore.findNewEntriesForDoc([], entry.docId);
912
- const createMeta = metas.find((m) => m.entryType === "doc_create");
913
- if (!createMeta)
914
- return null;
915
- const loaded = await localStore.getEntries([createMeta.id]);
916
- return loaded[0] ?? null;
917
- }
918
- catch {
919
- return null;
920
- }
921
- }
922
- /**
923
- * Build a database-open evaluator closure for a server store, or undefined
924
- * when the directory cannot evaluate the database-open policy (e.g. a
925
- * config-based directory without the access-control state chain). When the
926
- * tenant policy is `"directory-restricted"`, this rejects sync for any
927
- * database id that is not in the allowlist; `"directory"` is always allowed
928
- * and the tenant admin is exempt (resolved from the principal signing key).
929
- */
930
- buildDbAccessEvaluator(directory) {
931
- const evaluable = directory;
932
- if (typeof evaluable.evaluateDbAccessForSigningKey !== "function") {
933
- return undefined;
934
- }
935
- return async (principal, dbid) => {
936
- return evaluable.evaluateDbAccessForSigningKey({
937
- dbid,
938
- signingKey: principal.signingKey ?? "",
939
- });
940
- };
941
- }
942
- /**
943
- * Build a remote-wipe resolver closure for a server store, or undefined when
944
- * the directory does not support wipe directives (docs/accesscontrol.md §6.5).
945
- * Maps a wipe-targeted signing key to the admin-signed grant document id so
946
- * the server can serve only that document to the targeted device.
947
- */
948
- buildWipeGrantDocIdResolver(directory) {
949
- if (typeof directory.getWipeGrantDocId !== "function") {
950
- return undefined;
951
- }
952
- return (signingKey) => directory.getWipeGrantDocId(signingKey);
953
- }
954
- /**
955
- * Build a per-user revoked-key blacklist resolver for a server store, or
956
- * undefined when the directory cannot resolve revoked keys (e.g. a
957
- * config-based directory). Maps the authenticated principal's signing key to
958
- * the set of decryption key ids revoked for them at the directory head
959
- * (docs/accesscontrol.md §13).
960
- */
961
- buildRevokedKeyResolver(directory) {
962
- if (typeof directory.getRevokedDecryptionKeyIdsForSigningKey !== "function") {
963
- return undefined;
964
- }
965
- return async (principal) => {
966
- const ids = await directory.getRevokedDecryptionKeyIdsForSigningKey(principal.signingKey ?? "");
967
- return new Set(ids);
968
- };
969
- }
970
- async getStore(tenantId, dbId, storeKind) {
971
- const tenant = await this.getTenant(tenantId);
972
- return tenant.storeFactory.getStore(dbId, storeKind);
973
- }
974
- /**
975
- * Documents purged from `dbId` on this server (the persistent purge
976
- * registry). Peer pull replication refuses their entries.
977
- */
978
- getPurgedDocIds(tenantId, dbId) {
979
- return this.getPurgedDocRegistry(tenantId).getPurgedDocIds(dbId);
980
- }
981
- /** The (lazily-loaded, cached) purge registry for a tenant. */
982
- getPurgedDocRegistry(tenantId) {
983
- const normalizedId = tenantId.toLowerCase();
984
- let registry = this.purgedDocRegistries.get(normalizedId);
985
- if (!registry) {
986
- registry = new PurgedDocRegistry_1.PurgedDocRegistry((0, path_1.join)(this.resolveTenantDir(normalizedId), "purged-docs.json"));
987
- this.purgedDocRegistries.set(normalizedId, registry);
988
- }
989
- return registry;
39
+ }
40
+ exports.NodeServerStoreProvider = NodeServerStoreProvider;
41
+ function ensureDataDir(dataDir) {
42
+ if (!(0, fs_1.existsSync)(dataDir)) {
43
+ (0, fs_1.mkdirSync)(dataDir, { recursive: true });
44
+ console.log(`[TenantManager] Created data directory: ${dataDir}`);
990
45
  }
991
- /**
992
- * Execute any not-yet-processed document-history purge requests for a tenant
993
- * (docs/accesscontrol.md §13). Reads the admin-signed purge requests from the
994
- * tenant directory (server-readable `$publicinfos` envelope), physically
995
- * purges each requested document's history from the tenant's `docs` and
996
- * `attachments` stores, and records the purge in the persistent registry so
997
- * (a) it is never executed twice and (b) re-pushes of the purged docs are
998
- * rejected at sync time.
999
- *
1000
- * No-op when the server cannot read the directory (config-based fallback, or
1001
- * the directory does not expose the purge reader). Runs are serialized per
1002
- * tenant; concurrent callers share the in-flight promise.
1003
- */
1004
- async executePendingPurges(tenantId) {
1005
- const normalizedId = tenantId.toLowerCase();
1006
- const existing = this.purgeInFlight.get(normalizedId);
1007
- if (existing)
1008
- return existing;
1009
- const run = this.executePendingPurgesInternal(normalizedId).finally(() => {
1010
- this.purgeInFlight.delete(normalizedId);
46
+ return dataDir;
47
+ }
48
+ class TenantManager extends TenantManager_1.TenantManager {
49
+ constructor(dataDir, serverPassword) {
50
+ const dataStore = new FsServerDataStore_1.FsServerDataStore(ensureDataDir(dataDir));
51
+ super({
52
+ dataStore,
53
+ storeProvider: new NodeServerStoreProvider(dataDir, dataStore),
54
+ cryptoAdapter: new NodeCryptoAdapter_1.NodeCryptoAdapter(),
55
+ serverPassword,
1011
56
  });
1012
- this.purgeInFlight.set(normalizedId, run);
1013
- return run;
1014
- }
1015
- async executePendingPurgesInternal(normalizedId) {
1016
- const tenant = await this.getTenant(normalizedId);
1017
- if (!tenant.mindooTenant) {
1018
- // Config-based fallback: the server holds no $publicinfos directory and
1019
- // cannot read purge requests. Clients still purge locally on reconcile.
1020
- return;
1021
- }
1022
- let directory;
1023
- try {
1024
- directory = await tenant.mindooTenant.openDirectory();
1025
- }
1026
- catch (error) {
1027
- console.error(`[TenantManager] executePendingPurges: cannot open directory for ${normalizedId}:`, error);
1028
- return;
1029
- }
1030
- if (typeof directory.getRequestedDocHistoryPurges !== "function") {
1031
- return;
1032
- }
1033
- let requests;
1034
- try {
1035
- requests = await directory.getRequestedDocHistoryPurges();
1036
- }
1037
- catch (error) {
1038
- console.error(`[TenantManager] executePendingPurges: cannot read purge requests for ${normalizedId}:`, error);
1039
- return;
1040
- }
1041
- if (requests.length === 0)
1042
- return;
1043
- const registry = this.getPurgedDocRegistry(normalizedId);
1044
- const purgedNow = new Set();
1045
- const touchedDbIds = new Set();
1046
- for (const request of requests) {
1047
- if (registry.isRequestProcessed(request.purgeRequestDocId)) {
1048
- continue;
1049
- }
1050
- if (typeof request.dbId !== "string" || request.dbId.length === 0) {
1051
- continue;
1052
- }
1053
- for (const docId of request.docIds) {
1054
- if (typeof docId !== "string" || docId.length === 0)
1055
- continue;
1056
- // Denylist BEFORE purging: the sync push path consults the registry
1057
- // live, so from this moment re-pushes of the doc are rejected. Doing it
1058
- // after the store purges would leave a window in which the entries are
1059
- // already gone but a stale client could still re-push (and the server
1060
- // would re-accept) the very history that is being purged. The record is
1061
- // kept regardless of per-store outcome so re-pushes are rejected even
1062
- // if one store had nothing to purge.
1063
- registry.recordPurgedDoc(request.dbId, docId);
1064
- await this.purgeDocumentFromDatabase(normalizedId, request.dbId, docId);
1065
- purgedNow.add(`${request.dbId}\u0000${docId}`);
1066
- touchedDbIds.add(request.dbId);
1067
- }
1068
- registry.markRequestProcessed(request.purgeRequestDocId);
1069
- console.log(`[TenantManager] Executed purge request ${request.purgeRequestDocId} (${request.docIds.length} doc(s)) for ${normalizedId}/${request.dbId}`);
1070
- }
1071
- // A purge keeps attachment chunks that another document's history still
1072
- // references (an in-place copy). Once that document is purged as well,
1073
- // the earlier purge's leftovers are unreferenced: sweep them now, since a
1074
- // processed request is never executed again.
1075
- for (const dbId of touchedDbIds) {
1076
- for (const docId of registry.getPurgedDocIds(dbId)) {
1077
- if (!purgedNow.has(`${dbId}\u0000${docId}`)) {
1078
- await this.purgeDocumentFromDatabase(normalizedId, dbId, docId);
1079
- }
1080
- }
1081
- }
1082
- }
1083
- /**
1084
- * Purge one document from a database's docs and attachment stores. Attachment
1085
- * chunks still referenced by another document are kept (see
1086
- * `purgeDocumentHistory.ts`). Failures are logged, never thrown.
1087
- */
1088
- async purgeDocumentFromDatabase(tenantId, dbId, docId) {
1089
- try {
1090
- await (0, purgeDocumentHistory_1.purgeDocumentHistoryFromStores)({
1091
- docsStore: await this.getStore(tenantId, dbId, types_1.StoreKind.docs),
1092
- attachmentStore: await this.getStore(tenantId, dbId, types_1.StoreKind.attachments),
1093
- }, docId);
1094
- }
1095
- catch (error) {
1096
- console.error(`[TenantManager] executePendingPurges: failed to purge ${docId} in ${tenantId}/${dbId}:`, error);
1097
- }
57
+ this.dataDir = dataDir;
1098
58
  }
1099
- async getAuthService(tenantId) {
1100
- const tenant = await this.getTenant(tenantId);
1101
- return tenant.authService;
59
+ getDataDir() {
60
+ return this.dataDir;
1102
61
  }
1103
62
  getCryptoAdapter() {
1104
- return this.cryptoAdapter;
1105
- }
1106
- async reloadTenant(tenantId) {
1107
- const normalizedId = tenantId.toLowerCase();
1108
- this.loadedTenants.delete(normalizedId);
1109
- return this.getTenant(normalizedId);
1110
- }
1111
- clearCache() {
1112
- this.loadedTenants.clear();
1113
- console.log(`[TenantManager] Cleared tenant cache`);
1114
- }
1115
- // -----------------------------------------------------------------------
1116
- // Private helpers
1117
- // -----------------------------------------------------------------------
1118
- async loadTenant(tenantId) {
1119
- const configPath = this.resolveTenantConfigPath(tenantId);
1120
- if (!(0, fs_1.existsSync)(configPath)) {
1121
- throw new Error(`Tenant ${tenantId} not found: ${configPath}`);
1122
- }
1123
- const configJson = (0, fs_1.readFileSync)(configPath, "utf-8");
1124
- const config = JSON.parse(configJson);
1125
- const context = { tenantId, config };
1126
- const storeFactory = new StoreFactory_1.StoreFactory(tenantId, config, this.dataDir);
1127
- // Build the inner directory (real or config-based fallback)
1128
- let innerDirectory;
1129
- let mindooTenant;
1130
- const publicInfosKeys = await this.getStoredPublicInfosKeys(tenantId);
1131
- if (publicInfosKeys.length > 0 &&
1132
- this.serverIdentity &&
1133
- this.serverPassword) {
1134
- try {
1135
- const result = await this.createDirectoryTenant(tenantId, config, storeFactory, publicInfosKeys);
1136
- mindooTenant = result.tenant;
1137
- innerDirectory = await mindooTenant.openDirectory();
1138
- console.log(`[TenantManager] Loaded tenant ${tenantId} with real directory`);
1139
- }
1140
- catch (error) {
1141
- console.error(`[TenantManager] Failed to create real directory for ${tenantId}, falling back to config:`, error);
1142
- innerDirectory = new SimpleMindooDirectory(config);
1143
- }
1144
- }
1145
- else {
1146
- if (publicInfosKeys.length > 0 && !this.serverIdentity) {
1147
- console.log(`[TenantManager] Tenant ${tenantId} has $publicinfos keys but no server identity; using config-based directory`);
1148
- }
1149
- else {
1150
- console.log(`[TenantManager] No $publicinfos keys for ${tenantId}, using config-based directory`);
1151
- }
1152
- innerDirectory = new SimpleMindooDirectory(config);
1153
- }
1154
- // Wrap with CompositeMindooDirectory so trusted servers are also recognized
1155
- const directory = new CompositeMindooDirectory(innerDirectory, this.trustedServers, config.adminUsername
1156
- ? {
1157
- username: config.adminUsername,
1158
- signingPublicKey: config.adminSigningPublicKey,
1159
- encryptionPublicKey: config.adminEncryptionPublicKey,
1160
- }
1161
- : undefined);
1162
- const authService = new AuthenticationService_1.AuthenticationService(this.cryptoAdapter, directory, tenantId);
1163
- console.log(`[TenantManager] Loaded tenant: ${tenantId}`);
1164
- return {
1165
- context,
1166
- storeFactory,
1167
- authService,
1168
- directory,
1169
- mindooTenant,
1170
- serverStores: new Map(),
1171
- };
1172
- }
1173
- /**
1174
- * Create a real BaseMindooTenant for directory reading using the global
1175
- * server identity.
1176
- */
1177
- async createDirectoryTenant(tenantId, config, storeFactory, publicInfosKeys) {
1178
- const storeFactoryAdapter = new StoreFactoryAdapter(storeFactory);
1179
- const factory = new BaseMindooTenantFactory_1.BaseMindooTenantFactory(storeFactoryAdapter, this.cryptoAdapter);
1180
- const serverUser = this.serverIdentity;
1181
- // Create a KeyBag using the server user's encryption key
1182
- const keyBag = await this.createServerKeyBag(serverUser, this.serverPassword);
1183
- for (const publicInfosKey of this.mergeUniqueKeys(publicInfosKeys)) {
1184
- await keyBag.set("doc", tenantId, types_1.PUBLIC_INFOS_KEY_ID, publicInfosKey);
1185
- }
1186
- // Collect trusted server signing keys for additionalTrustedKeys
1187
- const openOptions = {};
1188
- if (this.trustedServers.length > 0) {
1189
- const trustedKeys = new Map();
1190
- for (const server of this.trustedServers) {
1191
- trustedKeys.set(server.signingPublicKey, true);
1192
- }
1193
- openOptions.additionalTrustedKeys = trustedKeys;
1194
- }
1195
- const tenant = await factory.openTenant(tenantId, config.adminSigningPublicKey, config.adminEncryptionPublicKey, serverUser, this.serverPassword, keyBag, openOptions);
1196
- return { tenant };
63
+ return super.getCryptoAdapter();
1197
64
  }
1198
65
  }
1199
66
  exports.TenantManager = TenantManager;