mindoodb 0.0.2 → 0.0.4

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 (305) hide show
  1. package/README-server.md +1001 -0
  2. package/README.md +89 -28
  3. package/dist/__mocks__/expo-standard-web-crypto.d.ts +10 -0
  4. package/dist/__mocks__/expo-standard-web-crypto.d.ts.map +1 -0
  5. package/dist/__mocks__/expo-standard-web-crypto.js +22 -0
  6. package/dist/__mocks__/expo-standard-web-crypto.js.map +1 -0
  7. package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts +8 -0
  8. package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts.map +1 -0
  9. package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js +16 -0
  10. package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js.map +1 -0
  11. package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts +1 -1
  12. package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts.map +1 -1
  13. package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js +2 -2
  14. package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js.map +1 -1
  15. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +30 -1
  16. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
  17. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +152 -5
  18. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
  19. package/dist/appendonlystores/network/HttpTransport.d.ts +17 -2
  20. package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
  21. package/dist/appendonlystores/network/HttpTransport.js +129 -0
  22. package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
  23. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +22 -2
  24. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
  25. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +110 -0
  26. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
  27. package/dist/appendonlystores/network/index.d.ts +1 -1
  28. package/dist/appendonlystores/network/index.d.ts.map +1 -1
  29. package/dist/appendonlystores/network/index.js.map +1 -1
  30. package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts +65 -0
  31. package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts.map +1 -0
  32. package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js +741 -0
  33. package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js.map +1 -0
  34. package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts +25 -0
  35. package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts.map +1 -0
  36. package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js +41 -0
  37. package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js.map +1 -0
  38. package/dist/browser/cache/IndexedDBLocalCacheStore.d.ts +21 -0
  39. package/dist/browser/cache/IndexedDBLocalCacheStore.d.ts.map +1 -0
  40. package/dist/browser/cache/IndexedDBLocalCacheStore.js +106 -0
  41. package/dist/browser/cache/IndexedDBLocalCacheStore.js.map +1 -0
  42. package/dist/browser/index.d.ts +8 -0
  43. package/dist/browser/index.d.ts.map +1 -1
  44. package/dist/browser/index.js +21 -1
  45. package/dist/browser/index.js.map +1 -1
  46. package/dist/core/BaseMindooDB.d.ts +115 -13
  47. package/dist/core/BaseMindooDB.d.ts.map +1 -1
  48. package/dist/core/BaseMindooDB.js +1232 -269
  49. package/dist/core/BaseMindooDB.js.map +1 -1
  50. package/dist/core/BaseMindooTenant.d.ts +69 -6
  51. package/dist/core/BaseMindooTenant.d.ts.map +1 -1
  52. package/dist/core/BaseMindooTenant.js +302 -109
  53. package/dist/core/BaseMindooTenant.js.map +1 -1
  54. package/dist/core/BaseMindooTenantDirectory.d.ts +13 -10
  55. package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
  56. package/dist/core/BaseMindooTenantDirectory.js +144 -27
  57. package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
  58. package/dist/core/BaseMindooTenantFactory.d.ts +27 -29
  59. package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
  60. package/dist/core/BaseMindooTenantFactory.js +185 -112
  61. package/dist/core/BaseMindooTenantFactory.js.map +1 -1
  62. package/dist/core/MindooDBServerAdmin.d.ts +238 -0
  63. package/dist/core/MindooDBServerAdmin.d.ts.map +1 -0
  64. package/dist/core/MindooDBServerAdmin.js +345 -0
  65. package/dist/core/MindooDBServerAdmin.js.map +1 -0
  66. package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +68 -0
  67. package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -0
  68. package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js +659 -0
  69. package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -0
  70. package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts +45 -3
  71. package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts.map +1 -1
  72. package/dist/core/appendonlystores/InMemoryContentAddressedStore.js +184 -23
  73. package/dist/core/appendonlystores/InMemoryContentAddressedStore.js.map +1 -1
  74. package/dist/core/appendonlystores/MaterializationPlanner.d.ts +49 -0
  75. package/dist/core/appendonlystores/MaterializationPlanner.d.ts.map +1 -0
  76. package/dist/core/appendonlystores/MaterializationPlanner.js +325 -0
  77. package/dist/core/appendonlystores/MaterializationPlanner.js.map +1 -0
  78. package/dist/core/appendonlystores/bloom.d.ts +85 -0
  79. package/dist/core/appendonlystores/bloom.d.ts.map +1 -0
  80. package/dist/core/appendonlystores/bloom.js +228 -0
  81. package/dist/core/appendonlystores/bloom.js.map +1 -0
  82. package/dist/core/appendonlystores/network/NetworkTransport.d.ts +37 -2
  83. package/dist/core/appendonlystores/network/NetworkTransport.d.ts.map +1 -1
  84. package/dist/core/appendonlystores/network/types.d.ts +12 -0
  85. package/dist/core/appendonlystores/network/types.d.ts.map +1 -1
  86. package/dist/core/appendonlystores/network/types.js.map +1 -1
  87. package/dist/core/appendonlystores/types.d.ts +206 -0
  88. package/dist/core/appendonlystores/types.d.ts.map +1 -1
  89. package/dist/core/automerge-adapter.d.ts +15 -0
  90. package/dist/core/automerge-adapter.d.ts.map +1 -0
  91. package/dist/core/automerge-adapter.js +73 -0
  92. package/dist/core/automerge-adapter.js.map +1 -0
  93. package/dist/core/cache/CacheManager.d.ts +75 -0
  94. package/dist/core/cache/CacheManager.d.ts.map +1 -0
  95. package/dist/core/cache/CacheManager.js +101 -0
  96. package/dist/core/cache/CacheManager.js.map +1 -0
  97. package/dist/core/cache/EncryptedLocalCacheStore.d.ts +41 -0
  98. package/dist/core/cache/EncryptedLocalCacheStore.d.ts.map +1 -0
  99. package/dist/core/cache/EncryptedLocalCacheStore.js +120 -0
  100. package/dist/core/cache/EncryptedLocalCacheStore.js.map +1 -0
  101. package/dist/core/cache/LocalCacheStore.d.ts +50 -0
  102. package/dist/core/cache/LocalCacheStore.d.ts.map +1 -0
  103. package/dist/core/cache/LocalCacheStore.js +39 -0
  104. package/dist/core/cache/LocalCacheStore.js.map +1 -0
  105. package/dist/core/crypto/pbkdf2Iterations.d.ts +12 -0
  106. package/dist/core/crypto/pbkdf2Iterations.d.ts.map +1 -0
  107. package/dist/core/crypto/pbkdf2Iterations.js +55 -0
  108. package/dist/core/crypto/pbkdf2Iterations.js.map +1 -0
  109. package/dist/core/crypto/privateKeyEncryption.d.ts +5 -0
  110. package/dist/core/crypto/privateKeyEncryption.d.ts.map +1 -0
  111. package/dist/core/crypto/privateKeyEncryption.js +87 -0
  112. package/dist/core/crypto/privateKeyEncryption.js.map +1 -0
  113. package/dist/core/index.d.ts +18 -6
  114. package/dist/core/index.d.ts.map +1 -1
  115. package/dist/core/index.js +42 -1
  116. package/dist/core/index.js.map +1 -1
  117. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.d.ts +4 -1
  118. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.d.ts.map +1 -1
  119. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.js +39 -11
  120. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.js.map +1 -1
  121. package/dist/core/indexing/virtualviews/ViewEntrySortKeyComparator.d.ts +1 -0
  122. package/dist/core/indexing/virtualviews/ViewEntrySortKeyComparator.d.ts.map +1 -1
  123. package/dist/core/indexing/virtualviews/ViewEntrySortKeyComparator.js +3 -0
  124. package/dist/core/indexing/virtualviews/ViewEntrySortKeyComparator.js.map +1 -1
  125. package/dist/core/indexing/virtualviews/VirtualView.d.ts +43 -0
  126. package/dist/core/indexing/virtualviews/VirtualView.d.ts.map +1 -1
  127. package/dist/core/indexing/virtualviews/VirtualView.js +173 -0
  128. package/dist/core/indexing/virtualviews/VirtualView.js.map +1 -1
  129. package/dist/core/indexing/virtualviews/VirtualViewEntryData.d.ts +7 -0
  130. package/dist/core/indexing/virtualviews/VirtualViewEntryData.d.ts.map +1 -1
  131. package/dist/core/indexing/virtualviews/VirtualViewEntryData.js +28 -0
  132. package/dist/core/indexing/virtualviews/VirtualViewEntryData.js.map +1 -1
  133. package/dist/core/keys/KeyBag.d.ts +56 -12
  134. package/dist/core/keys/KeyBag.d.ts.map +1 -1
  135. package/dist/core/keys/KeyBag.js +156 -70
  136. package/dist/core/keys/KeyBag.js.map +1 -1
  137. package/dist/core/keys/KeyContext.d.ts +4 -0
  138. package/dist/core/keys/KeyContext.d.ts.map +1 -0
  139. package/dist/core/keys/KeyContext.js +17 -0
  140. package/dist/core/keys/KeyContext.js.map +1 -0
  141. package/dist/core/types.d.ts +512 -60
  142. package/dist/core/types.d.ts.map +1 -1
  143. package/dist/core/types.js +9 -1
  144. package/dist/core/types.js.map +1 -1
  145. package/dist/core/uri/MindooURI.d.ts +58 -0
  146. package/dist/core/uri/MindooURI.d.ts.map +1 -0
  147. package/dist/core/uri/MindooURI.js +169 -0
  148. package/dist/core/uri/MindooURI.js.map +1 -0
  149. package/dist/core/userid/index.d.ts +0 -10
  150. package/dist/core/userid/index.d.ts.map +1 -1
  151. package/dist/core/utils/idGeneration.d.ts.map +1 -1
  152. package/dist/core/utils/idGeneration.js +37 -3
  153. package/dist/core/utils/idGeneration.js.map +1 -1
  154. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +544 -0
  155. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -0
  156. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +1358 -0
  157. package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -0
  158. package/dist/node/attachments/writeAttachmentToFile.d.ts +15 -0
  159. package/dist/node/attachments/writeAttachmentToFile.d.ts.map +1 -0
  160. package/dist/node/attachments/writeAttachmentToFile.js +73 -0
  161. package/dist/node/attachments/writeAttachmentToFile.js.map +1 -0
  162. package/dist/node/cache/FileSystemLocalCacheStore.d.ts +17 -0
  163. package/dist/node/cache/FileSystemLocalCacheStore.d.ts.map +1 -0
  164. package/dist/node/cache/FileSystemLocalCacheStore.js +128 -0
  165. package/dist/node/cache/FileSystemLocalCacheStore.js.map +1 -0
  166. package/dist/node/cli/change-identity-password.d.ts +7 -0
  167. package/dist/node/cli/change-identity-password.d.ts.map +1 -0
  168. package/dist/node/cli/change-identity-password.js +67 -0
  169. package/dist/node/cli/change-identity-password.js.map +1 -0
  170. package/dist/node/cli/cli-utils.d.ts +16 -0
  171. package/dist/node/cli/cli-utils.d.ts.map +1 -0
  172. package/dist/node/cli/cli-utils.js +113 -0
  173. package/dist/node/cli/cli-utils.js.map +1 -0
  174. package/dist/node/cli/identity-export-public.d.ts +4 -0
  175. package/dist/node/cli/identity-export-public.d.ts.map +1 -0
  176. package/dist/node/cli/identity-export-public.js +55 -0
  177. package/dist/node/cli/identity-export-public.js.map +1 -0
  178. package/dist/node/cli/identity-info.d.ts +4 -0
  179. package/dist/node/cli/identity-info.d.ts.map +1 -0
  180. package/dist/node/cli/identity-info.js +57 -0
  181. package/dist/node/cli/identity-info.js.map +1 -0
  182. package/dist/node/index.d.ts +2 -0
  183. package/dist/node/index.d.ts.map +1 -1
  184. package/dist/node/index.js +8 -1
  185. package/dist/node/index.js.map +1 -1
  186. package/dist/node/server/CapabilityMatcher.d.ts +45 -0
  187. package/dist/node/server/CapabilityMatcher.d.ts.map +1 -0
  188. package/dist/node/server/CapabilityMatcher.js +117 -0
  189. package/dist/node/server/CapabilityMatcher.js.map +1 -0
  190. package/dist/node/server/MindooDBServer.d.ts +87 -0
  191. package/dist/node/server/MindooDBServer.d.ts.map +1 -0
  192. package/dist/node/server/MindooDBServer.js +1040 -0
  193. package/dist/node/server/MindooDBServer.js.map +1 -0
  194. package/dist/node/server/ServerSync.d.ts +41 -0
  195. package/dist/node/server/ServerSync.d.ts.map +1 -0
  196. package/dist/node/server/ServerSync.js +187 -0
  197. package/dist/node/server/ServerSync.js.map +1 -0
  198. package/dist/node/server/StoreFactory.d.ts +45 -0
  199. package/dist/node/server/StoreFactory.d.ts.map +1 -0
  200. package/dist/node/server/StoreFactory.js +89 -0
  201. package/dist/node/server/StoreFactory.js.map +1 -0
  202. package/dist/node/server/SystemAdminAuth.d.ts +65 -0
  203. package/dist/node/server/SystemAdminAuth.d.ts.map +1 -0
  204. package/dist/node/server/SystemAdminAuth.js +200 -0
  205. package/dist/node/server/SystemAdminAuth.js.map +1 -0
  206. package/dist/node/server/SystemIpAllowlist.d.ts +21 -0
  207. package/dist/node/server/SystemIpAllowlist.d.ts.map +1 -0
  208. package/dist/node/server/SystemIpAllowlist.js +210 -0
  209. package/dist/node/server/SystemIpAllowlist.js.map +1 -0
  210. package/dist/node/server/TenantManager.d.ts +85 -0
  211. package/dist/node/server/TenantManager.d.ts.map +1 -0
  212. package/dist/node/server/TenantManager.js +493 -0
  213. package/dist/node/server/TenantManager.js.map +1 -0
  214. package/dist/node/server/add-to-network.d.ts +20 -0
  215. package/dist/node/server/add-to-network.d.ts.map +1 -0
  216. package/dist/node/server/add-to-network.js +275 -0
  217. package/dist/node/server/add-to-network.js.map +1 -0
  218. package/dist/node/server/config.d.ts +51 -0
  219. package/dist/node/server/config.d.ts.map +1 -0
  220. package/dist/node/server/config.js +218 -0
  221. package/dist/node/server/config.js.map +1 -0
  222. package/dist/node/server/resolveServerPassword.d.ts +7 -0
  223. package/dist/node/server/resolveServerPassword.d.ts.map +1 -0
  224. package/dist/node/server/resolveServerPassword.js +25 -0
  225. package/dist/node/server/resolveServerPassword.js.map +1 -0
  226. package/dist/node/server/server.d.ts +22 -0
  227. package/dist/node/server/server.d.ts.map +1 -0
  228. package/dist/node/server/server.js +225 -0
  229. package/dist/node/server/server.js.map +1 -0
  230. package/dist/node/server/serverinit.d.ts +21 -0
  231. package/dist/node/server/serverinit.d.ts.map +1 -0
  232. package/dist/node/server/serverinit.js +317 -0
  233. package/dist/node/server/serverinit.js.map +1 -0
  234. package/dist/node/server/types.d.ts +162 -0
  235. package/dist/node/server/types.d.ts.map +1 -0
  236. package/dist/node/server/types.js +24 -0
  237. package/dist/node/server/types.js.map +1 -0
  238. package/dist/node/server/validation.d.ts +47 -0
  239. package/dist/node/server/validation.d.ts.map +1 -0
  240. package/dist/node/server/validation.js +114 -0
  241. package/dist/node/server/validation.js.map +1 -0
  242. package/dist/reactnative/cache/MMKVLocalCacheStore.d.ts +48 -0
  243. package/dist/reactnative/cache/MMKVLocalCacheStore.d.ts.map +1 -0
  244. package/dist/reactnative/cache/MMKVLocalCacheStore.js +113 -0
  245. package/dist/reactnative/cache/MMKVLocalCacheStore.js.map +1 -0
  246. package/dist/reactnative/crypto/AESAdapter.d.ts +18 -0
  247. package/dist/reactnative/crypto/AESAdapter.d.ts.map +1 -0
  248. package/dist/reactnative/crypto/AESAdapter.js +112 -0
  249. package/dist/reactnative/crypto/AESAdapter.js.map +1 -0
  250. package/dist/reactnative/crypto/Ed25519Adapter.d.ts +34 -0
  251. package/dist/reactnative/crypto/Ed25519Adapter.d.ts.map +1 -0
  252. package/dist/reactnative/crypto/Ed25519Adapter.js +386 -0
  253. package/dist/reactnative/crypto/Ed25519Adapter.js.map +1 -0
  254. package/dist/reactnative/crypto/ExpoGoCryptoAdapter.d.ts +23 -0
  255. package/dist/reactnative/crypto/ExpoGoCryptoAdapter.d.ts.map +1 -0
  256. package/dist/reactnative/crypto/ExpoGoCryptoAdapter.js +51 -0
  257. package/dist/reactnative/crypto/ExpoGoCryptoAdapter.js.map +1 -0
  258. package/dist/reactnative/crypto/HMACAdapter.d.ts +13 -0
  259. package/dist/reactnative/crypto/HMACAdapter.d.ts.map +1 -0
  260. package/dist/reactnative/crypto/HMACAdapter.js +50 -0
  261. package/dist/reactnative/crypto/HMACAdapter.js.map +1 -0
  262. package/dist/reactnative/crypto/KeyFormatConverter.d.ts +21 -0
  263. package/dist/reactnative/crypto/KeyFormatConverter.d.ts.map +1 -0
  264. package/dist/reactnative/crypto/KeyFormatConverter.js +72 -0
  265. package/dist/reactnative/crypto/KeyFormatConverter.js.map +1 -0
  266. package/dist/reactnative/crypto/PBKDF2Adapter.d.ts +19 -0
  267. package/dist/reactnative/crypto/PBKDF2Adapter.d.ts.map +1 -0
  268. package/dist/reactnative/crypto/PBKDF2Adapter.js +67 -0
  269. package/dist/reactnative/crypto/PBKDF2Adapter.js.map +1 -0
  270. package/dist/reactnative/crypto/QuickCryptoAdapter.d.ts +37 -0
  271. package/dist/reactnative/crypto/QuickCryptoAdapter.d.ts.map +1 -0
  272. package/dist/reactnative/crypto/QuickCryptoAdapter.js +72 -0
  273. package/dist/reactnative/crypto/QuickCryptoAdapter.js.map +1 -0
  274. package/dist/reactnative/crypto/RSAAdapter.d.ts +38 -0
  275. package/dist/reactnative/crypto/RSAAdapter.d.ts.map +1 -0
  276. package/dist/reactnative/crypto/RSAAdapter.js +184 -0
  277. package/dist/reactnative/crypto/RSAAdapter.js.map +1 -0
  278. package/dist/reactnative/crypto/ReactNativeCryptoAdapter.d.ts +33 -0
  279. package/dist/reactnative/crypto/ReactNativeCryptoAdapter.d.ts.map +1 -0
  280. package/dist/reactnative/crypto/ReactNativeCryptoAdapter.js +61 -0
  281. package/dist/reactnative/crypto/ReactNativeCryptoAdapter.js.map +1 -0
  282. package/dist/reactnative/crypto/SHA256Adapter.d.ts +9 -0
  283. package/dist/reactnative/crypto/SHA256Adapter.d.ts.map +1 -0
  284. package/dist/reactnative/crypto/SHA256Adapter.js +29 -0
  285. package/dist/reactnative/crypto/SHA256Adapter.js.map +1 -0
  286. package/dist/reactnative/crypto/SubtleCryptoPolyfill.d.ts +63 -0
  287. package/dist/reactnative/crypto/SubtleCryptoPolyfill.d.ts.map +1 -0
  288. package/dist/reactnative/crypto/SubtleCryptoPolyfill.js +350 -0
  289. package/dist/reactnative/crypto/SubtleCryptoPolyfill.js.map +1 -0
  290. package/dist/reactnative/index.d.ts +9 -0
  291. package/dist/reactnative/index.d.ts.map +1 -0
  292. package/dist/reactnative/index.js +41 -0
  293. package/dist/reactnative/index.js.map +1 -0
  294. package/package.json +63 -7
  295. package/patches/PATCHES.md +111 -0
  296. package/patches/react-native+0.76.9.patch +34 -0
  297. package/patches/react-native-quick-crypto+1.0.7.patch +42 -0
  298. package/scripts/cli.js +21 -0
  299. package/scripts/setup-react-native.js +228 -0
  300. package/templates/reactnative/App.js +252 -0
  301. package/templates/reactnative/app.json +29 -0
  302. package/templates/reactnative/babel.config.js +6 -0
  303. package/templates/reactnative/index.js +26 -0
  304. package/templates/reactnative/metro.config.snippet.js +39 -0
  305. package/templates/reactnative/mindoodb-polyfills.js +116 -0
@@ -0,0 +1,1001 @@
1
+ # MindooDB reference server
2
+
3
+ Node.js/Express sources: `src/node/server/`. Docker: `src/node/server/Dockerfile`.
4
+
5
+ A Node.js/Express server implementing the MindooDB sync API with:
6
+
7
+ - **Client-to-server sync** — clients push/pull encrypted entries
8
+ - **Server-to-server mirroring** — servers relay ciphertext without decryption
9
+ - **Capabilities-based system admin security** — challenge/response auth with JWT and fine-grained route authorization
10
+ - **Multi-tenant** — each tenant is isolated with its own config, keybag, and stores
11
+
12
+ ## Prerequisites
13
+
14
+ - **Docker** — the recommended way to build and run the server
15
+ - Node.js 20 or later (only needed for local development without Docker)
16
+
17
+ ## Quick Start
18
+
19
+ The fastest way to get a server running is the interactive setup script. It prompts for a server name, password, data directory, and bind address, then builds the Docker image and initialises the server identity (including optional system admin creation):
20
+
21
+ ```bash
22
+ # 1. Clone and enter the repo
23
+ git clone https://github.com/klehmann/MindooDB.git && cd MindooDB
24
+
25
+ # 2. Run the interactive setup
26
+ bash serversetup.sh
27
+
28
+ # 3. Start the server
29
+ docker compose up -d
30
+
31
+ # 4. Verify
32
+ curl http://localhost:1661/health
33
+ ```
34
+
35
+ The setup script:
36
+ - builds the `mindoodb-server` Docker image
37
+ - creates the data directory (`../mindoodb-data/server`) and password file (`../mindoodb-data/.server_unlock`, mode 600)
38
+ - initialises the server identity and optionally creates a system admin keypair interactively
39
+ - writes a `docker-compose.override.yml` with your current host uid/gid, bind mounts, and one or more published port bindings based on your chosen bind address
40
+
41
+ After setup, manage the server with:
42
+
43
+ ```bash
44
+ docker compose up -d # start
45
+ docker compose down # stop
46
+ docker compose logs -f # follow logs
47
+ docker compose up -d --build # rebuild image and restart
48
+ ```
49
+
50
+ For identity utilities on a Docker-deployed server, use the wrapper script from the repository root:
51
+
52
+ ```bash
53
+ ./mindoodb-cli.sh identity:info server.identity.json
54
+ ./mindoodb-cli.sh identity:change-password server.identity.json
55
+ ./mindoodb-cli.sh identity:export-public system-admin-cn-sysadmin-o-myorg.identity.json --output ./system-admin.public-identity.json
56
+ ```
57
+
58
+ Files inside the mounted server data directory can be referenced by filename alone. Running `./mindoodb-cli.sh` without arguments prints a command overview, and each `identity:*` command prints its own help when called without the required parameters.
59
+
60
+ ### What was created on disk?
61
+
62
+ After running `serversetup.sh` (with system admin creation), the data directory contains:
63
+
64
+ ```
65
+ ../mindoodb-data/server/
66
+ ├── server.identity.json # Server keypair (Ed25519 + RSA, password-encrypted)
67
+ ├── config.json # Admin access rules — who can call which /system/* endpoints
68
+ ├── trusted-servers.json # Public keys of remote servers trusted for sync (initially empty)
69
+ └── system-admin-cn-sysadmin-o-myorg.identity.json # System admin keypair (password-encrypted)
70
+ ```
71
+
72
+ The most important file for day-to-day operations is **`config.json`**. After a fresh setup with one system admin, it looks like this:
73
+
74
+ ```json
75
+ {
76
+ "capabilities": {
77
+ "ALL:/system/*": [
78
+ {
79
+ "username": "cn=sysadmin/o=myorg",
80
+ "publicsignkey": "-----BEGIN PUBLIC KEY-----\nMCow...base64...\n-----END PUBLIC KEY-----"
81
+ }
82
+ ]
83
+ }
84
+ }
85
+ ```
86
+
87
+ This single rule grants the admin `cn=sysadmin/o=myorg` full access to **all** `/system/*` endpoints -- tenant management, trusted server management, config updates, and everything else. The next section explains how to customize these rules.
88
+
89
+ ### What's next?
90
+
91
+ Once the server is healthy, a nice real-world workflow is:
92
+
93
+ 1. Create the tenant locally. This generates the tenant's own admin identity (`result.adminUser`).
94
+ 2. Share the tenant admin's **public** identity with the server owner.
95
+ 3. The server owner grants that admin access to `POST /system/tenants/...` via `MindooDBServerAdmin`, after which the tenant admin can publish the tenant directly.
96
+
97
+ ### 1. Create the tenant locally and export a public identity
98
+
99
+ ```typescript
100
+ import { writeFileSync } from "fs";
101
+ import { BaseMindooTenantFactory, InMemoryContentAddressedStoreFactory } from "mindoodb";
102
+
103
+ const factory = new BaseMindooTenantFactory(new InMemoryContentAddressedStoreFactory());
104
+
105
+ const result = await factory.createTenant({
106
+ tenantId: "acme",
107
+ adminName: "cn=admin/o=acme",
108
+ adminPassword: "admin-pass",
109
+ userName: "cn=alice/o=acme",
110
+ userPassword: "alice-pass",
111
+ });
112
+
113
+ const tenantAdminPublic = factory.toPublicUserId(result.adminUser);
114
+
115
+ writeFileSync(
116
+ "./acme-admin.public-identity.json",
117
+ JSON.stringify(tenantAdminPublic, null, 2),
118
+ "utf-8",
119
+ );
120
+ ```
121
+
122
+ The file `acme-admin.public-identity.json` contains only the tenant admin's public keys, so it can be shared with the server owner.
123
+
124
+ ### 2. Server owner grants tenant-creation access
125
+
126
+ The server owner loads the tenant admin's public identity and grants access for one tenant, a prefix, or all tenants:
127
+
128
+ ```typescript
129
+ import { readFileSync } from "fs";
130
+ import {
131
+ BaseMindooTenantFactory,
132
+ InMemoryContentAddressedStoreFactory,
133
+ MindooDBServerAdmin,
134
+ } from "mindoodb";
135
+ import { NodeCryptoAdapter } from "mindoodb/node";
136
+
137
+ const factory = new BaseMindooTenantFactory(new InMemoryContentAddressedStoreFactory());
138
+ const tenantAdminPublic = JSON.parse(
139
+ readFileSync("./acme-admin.public-identity.json", "utf-8"),
140
+ );
141
+ const systemAdminIdentity = JSON.parse(
142
+ readFileSync(
143
+ "../mindoodb-data/server/system-admin-cn-sysadmin-o-myorg.identity.json",
144
+ "utf-8",
145
+ ),
146
+ );
147
+
148
+ const serverAdmin = new MindooDBServerAdmin({
149
+ serverUrl: "http://localhost:1661",
150
+ systemAdminUser: systemAdminIdentity,
151
+ systemAdminPassword: "sysadmin-pass",
152
+ cryptoAdapter: new NodeCryptoAdapter(),
153
+ });
154
+
155
+ await serverAdmin.grantSystemAdminAccess(
156
+ {
157
+ username: tenantAdminPublic.username,
158
+ publicsignkey: tenantAdminPublic.userSigningPublicKey,
159
+ },
160
+ [
161
+ "POST:/system/tenants/acme", // exactly one tenant
162
+ // "POST:/system/tenants/customer-*", // or a naming prefix
163
+ // "POST:/system/tenants/*", // or all tenants
164
+ ],
165
+ );
166
+ ```
167
+
168
+ At that point the tenant admin is recognized by `/system/auth/challenge` and is allowed to create only the tenants covered by the granted rules.
169
+
170
+ ### 3. Tenant admin publishes the tenant
171
+
172
+ Now the tenant admin can authenticate with their own private identity and publish the tenant:
173
+
174
+ ```typescript
175
+ await result.tenant.publishToServer("http://localhost:1661", {
176
+ systemAdminUser: result.adminUser,
177
+ systemAdminPassword: "admin-pass",
178
+ adminUsername: result.adminUser.username,
179
+ registerUsers: [factory.toPublicUserId(result.appUser)],
180
+ });
181
+ ```
182
+
183
+ Here, `systemAdminUser` is the delegated tenant admin identity, while `adminUsername: result.adminUser.username` tells the server which tenant admin identity should be stored in the new tenant's `config.json`.
184
+
185
+ ```typescript
186
+ const remoteStore = await result.tenant.connectToServer(
187
+ "http://localhost:1661",
188
+ "main",
189
+ );
190
+
191
+ const db = await result.tenant.openDB("main");
192
+
193
+ await db.pushChangesTo(remoteStore);
194
+
195
+ await db.pullChangesFrom(remoteStore);
196
+ await db.syncStoreChanges();
197
+ ```
198
+
199
+ ## How `config.json` controls admin access
200
+
201
+ MindooDB uses a **capabilities-based model** for server administration. Instead of a single shared API key, each admin has their own Ed25519 keypair, and `config.json` declares exactly which endpoints each admin is allowed to call.
202
+
203
+ The server has two authentication tiers:
204
+
205
+ 1. **System admin** — `config.json` capabilities + Ed25519 challenge/response + short-lived JWT for `/system/*`. Optional **`MINDOODB_ADMIN_ALLOWED_IPS`** restricts which client IPs may use the `/system/*` HTTP surface.
206
+ 2. **User (per-tenant)** — Ed25519 challenge-response for sync endpoints (`/:tenantId/sync/*`). Users are authenticated against the tenant directory (admin-signed).
207
+
208
+ This section covers tier 1 -- system admin access. For the design rationale behind this model, see [Server Security](docs/server-security.md).
209
+
210
+ ### Rule format
211
+
212
+ Each rule in `config.json` is a key-value pair where:
213
+
214
+ - The **key** is `METHOD:PATHPATTERN` — an HTTP method (or `ALL`) paired with a URL path pattern
215
+ - The **value** is an array of **principals** (admin identities), each identified by `username` + `publicsignkey`
216
+
217
+ ```json
218
+ {
219
+ "capabilities": {
220
+ "METHOD:PATHPATTERN": [
221
+ { "username": "<admin-username>", "publicsignkey": "<ed25519-public-key-pem>" }
222
+ ]
223
+ }
224
+ }
225
+ ```
226
+
227
+ **METHOD** can be `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, or `ALL` (matches any HTTP method).
228
+
229
+ **PATHPATTERN** is a URL path that may end with `*` to match any remaining characters.
230
+
231
+ ### Wildcard patterns
232
+
233
+ | Rule | What it grants |
234
+ |------|---------------|
235
+ | `ALL:/system/*` | Full access to all system endpoints |
236
+ | `POST:/system/tenants/*` | Can create any tenant (but not delete, update, or list) |
237
+ | `POST:/system/tenants/acme` | Can only create the specific tenant `acme` |
238
+ | `PUT:/system/tenants/company-*` | Can update any tenant whose ID starts with `company-` |
239
+ | `GET:/system/tenants` | Read-only: can list tenants but not modify anything |
240
+ | `DELETE:/system/tenants/*` | Can delete any tenant |
241
+
242
+ ### Multiple admins with different roles
243
+
244
+ A realistic production config might look like this:
245
+
246
+ ```json
247
+ {
248
+ "capabilities": {
249
+ "ALL:/system/*": [
250
+ { "username": "cn=admin/o=myorg", "publicsignkey": "<key-admin>" }
251
+ ],
252
+ "POST:/system/tenants/*": [
253
+ { "username": "cn=provisioner/o=myorg", "publicsignkey": "<key-provisioner>" }
254
+ ],
255
+ "GET:/system/tenants": [
256
+ { "username": "cn=auditor/o=myorg", "publicsignkey": "<key-auditor>" }
257
+ ]
258
+ }
259
+ }
260
+ ```
261
+
262
+ - **`cn=admin`** is the super-admin with full access
263
+ - **`cn=provisioner`** can create tenants but cannot delete them, manage trusted servers, or change the config
264
+ - **`cn=auditor`** can list tenants (read-only) but has no write access
265
+
266
+ To delegate tenant management for a specific prefix to a team, add a scoped rule:
267
+
268
+ ```json
269
+ "PUT:/system/tenants/team-alpha-*": [
270
+ { "username": "cn=lead/o=alpha", "publicsignkey": "<key-lead>" }
271
+ ]
272
+ ```
273
+
274
+ ### How matching works at request time
275
+
276
+ On each `/system/*` request the server:
277
+
278
+ 1. Extracts the HTTP method and path from the request
279
+ 2. Validates the JWT and extracts `username` + `publicsignkey` from its payload
280
+ 3. Finds all capability rules whose method and path pattern match
281
+ 4. Unions all principal entries from matching rules
282
+ 5. **Allows** the request if any entry has **both** a matching `username` AND `publicsignkey`
283
+
284
+ A principal is identified by the combination of username + public key. Two admins may share a username but have different keys -- they are treated as distinct identities.
285
+
286
+ ### Updating config at runtime
287
+
288
+ You do not need to restart the server to change access rules. Use the system admin API:
289
+
290
+ - **`GET /system/config`** — read the current config
291
+ - **`PUT /system/config`** — replace the config (takes effect immediately)
292
+ - **`GET /system/config/backups`** — list previous config snapshots created by runtime updates
293
+ - **`GET /system/config/backups/:backupFile`** — read one previous validated config snapshot
294
+
295
+ Before overwriting, the server creates a timestamped backup (e.g., `config.2026-03-27T16-30-45.123Z.json`). Self-lockout protection rejects any change that would remove the calling admin's own `PUT /system/config` access.
296
+
297
+ In Node you can use `MindooDBServerAdmin` instead of raw HTTP:
298
+
299
+ ```typescript
300
+ const admin = new MindooDBServerAdmin({
301
+ serverUrl: "http://localhost:1661",
302
+ systemAdminUser: adminIdentity,
303
+ systemAdminPassword: "your-admin-password",
304
+ cryptoAdapter: new NodeCryptoAdapter(),
305
+ });
306
+
307
+ await admin.grantSystemAdminAccess(
308
+ {
309
+ username: "cn=newauditor/o=myorg",
310
+ publicsignkey: "<new-auditor-key>",
311
+ },
312
+ ["GET:/system/tenants"],
313
+ );
314
+
315
+ const access = await admin.findSystemAdminAccess({
316
+ username: "cn=newauditor/o=myorg",
317
+ publicsignkey: "<new-auditor-key>",
318
+ });
319
+
320
+ const backups = await admin.listConfigBackups();
321
+ const previousConfig = await admin.getConfigBackup(backups[0].file);
322
+ ```
323
+
324
+ #### Demo server `config.json` examples
325
+
326
+ For isolated demo environments, MindooDB Server also supports a special wildcard principal:
327
+
328
+ ```json
329
+ { "username": "*", "publicsignkey": "*" }
330
+ ```
331
+
332
+ This wildcard is intentionally narrow:
333
+
334
+ - It is only valid on `POST:/system/tenants/...` capability rules
335
+ - It allows any username + any signing key to authenticate for tenant creation
336
+ - It does **not** unlock `GET /system/tenants`, `PUT /system/config`, or any other `/system/*` route
337
+
338
+ Open demo server (allow creation of any tenant name):
339
+
340
+ ```json
341
+ {
342
+ "capabilities": {
343
+ "POST:/system/tenants/*": [
344
+ { "username": "*", "publicsignkey": "*" }
345
+ ]
346
+ }
347
+ }
348
+ ```
349
+
350
+ Prefix-restricted demo server (allow only tenant names starting with `demo_`):
351
+
352
+ ```json
353
+ {
354
+ "capabilities": {
355
+ "POST:/system/tenants/demo_*": [
356
+ { "username": "*", "publicsignkey": "*" }
357
+ ]
358
+ }
359
+ }
360
+ ```
361
+
362
+ These examples are useful for public demos or temporary onboarding servers, but they are intentionally less strict than normal production setups. For regular servers, prefer explicit principals with real public keys.
363
+
364
+ For key rotation, adding/removing admins, and the full authentication flow (challenge/response, JWT lifecycle), see [Server Security](docs/server-security.md).
365
+
366
+ ## Walkthrough: Multi-Server Setup
367
+
368
+ MindooDB servers can mirror encrypted data between each other. This section covers the full workflow: initializing servers, establishing trust, and configuring per-tenant sync.
369
+
370
+ ### 1. Initialize servers
371
+
372
+ Run `bash serversetup.sh` on each machine (or in separate data directories for local testing):
373
+
374
+ ```bash
375
+ # Server 1
376
+ bash serversetup.sh
377
+ # Choose: name=server1, data dir=../mindoodb-data-s1, bind 127.0.0.1
378
+
379
+ # Server 2
380
+ bash serversetup.sh
381
+ # Choose: name=server2, data dir=../mindoodb-data-s2, bind 127.0.0.1
382
+ ```
383
+
384
+ Both runs print the server's public keys. Start both servers:
385
+
386
+ ```bash
387
+ docker compose up -d
388
+ ```
389
+
390
+ ### 2. Server discovery
391
+
392
+ Every initialized server exposes its public identity at a well-known URL:
393
+
394
+ ```bash
395
+ curl https://server1.example.com/.well-known/mindoodb-server-info
396
+ ```
397
+
398
+ ```json
399
+ {
400
+ "name": "CN=server1",
401
+ "signingPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
402
+ "encryptionPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
403
+ }
404
+ ```
405
+
406
+ This eliminates the need to manually copy public keys between servers.
407
+
408
+ ### 3. Establish trust
409
+
410
+ The `add-to-network` CLI automates mutual trust exchange. It fetches each server's public keys from `/.well-known/mindoodb-server-info` and calls `POST /system/trusted-servers` on each side using your system admin identity.
411
+
412
+ ```bash
413
+ printf '%s' 'admin-pass' > ./.admin-password && chmod 600 ./.admin-password
414
+ npm run server:add-to-network -- \
415
+ --new-server http://localhost:3001 \
416
+ --servers http://localhost:1661 \
417
+ --identity ../mindoodb-data-s1/system-admin-cn-sysadmin-o-myorg.identity.json \
418
+ --password-file ./.admin-password
419
+ ```
420
+
421
+ The CLI authenticates with the `--identity` file against every server it contacts (`--new-server` **and** each URL in `--servers`), so that identity's principal must be listed in each server's `config.json`. If a server pair is already trusted, the CLI skips it instead of failing.
422
+
423
+ **Secrets:** Prefer **`--password-file`** so secrets are not placed in the process environment. Use **`MINDOODB_SYSTEM_ADMIN_PASSWORD`** only when you must script without files. A leading space before the command or `HISTCONTROL=ignorespace` reduces shell-history leakage.
424
+
425
+ Alternatively, you can exchange trust manually via curl:
426
+
427
+ ```bash
428
+ curl -X POST http://server1:1661/system/trusted-servers \
429
+ -H "Content-Type: application/json" \
430
+ -H "Authorization: Bearer $SYSTEM_ADMIN_JWT" \
431
+ -d '{
432
+ "name": "CN=server2",
433
+ "signingPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
434
+ "encryptionPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
435
+ }'
436
+ ```
437
+
438
+ Trust changes take effect immediately -- no restart required.
439
+
440
+ ### 4. Publish tenant to servers
441
+
442
+ Reuse the same system admin identity that is authorized in each server's `config.json`:
443
+
444
+ ```typescript
445
+ await result.tenant.publishToServer("http://server1:1661", {
446
+ systemAdminUser: systemAdminIdentity,
447
+ systemAdminPassword: "sysadmin-pass",
448
+ adminUsername: result.adminUser.username,
449
+ });
450
+
451
+ await result.tenant.publishToServer("http://server2:3001", {
452
+ systemAdminUser: systemAdminIdentity,
453
+ systemAdminPassword: "sysadmin-pass",
454
+ adminUsername: result.adminUser.username,
455
+ });
456
+ ```
457
+
458
+ ### 5. Configure per-tenant sync
459
+
460
+ After trust is established, configure which tenants each server syncs. This gives full control over sync topology -- not every server needs to sync every tenant.
461
+
462
+ **Add a sync server for a tenant:**
463
+
464
+ ```bash
465
+ curl -X POST http://server1:1661/system/tenants/acme/sync-servers \
466
+ -H "Content-Type: application/json" \
467
+ -H "Authorization: Bearer $SYSTEM_ADMIN_JWT" \
468
+ -d '{
469
+ "name": "CN=server2",
470
+ "url": "http://server2:3001",
471
+ "syncIntervalMs": 60000,
472
+ "databases": ["directory", "main"]
473
+ }'
474
+ ```
475
+
476
+ The `databases` field is required and controls which databases are synced with the remote server. The `name` field identifies the remote server (must match the trusted server name). If a server with the same name already exists for the tenant, it is updated.
477
+
478
+ **List sync servers for a tenant:**
479
+
480
+ ```bash
481
+ curl http://server1:1661/system/tenants/acme/sync-servers \
482
+ -H "Authorization: Bearer $SYSTEM_ADMIN_JWT"
483
+ ```
484
+
485
+ **Remove a sync server from a tenant:**
486
+
487
+ ```bash
488
+ curl -X DELETE http://server1:1661/system/tenants/acme/sync-servers/CN%3Dserver2 \
489
+ -H "Authorization: Bearer $SYSTEM_ADMIN_JWT"
490
+ ```
491
+
492
+ Note: the server name in the URL must be percent-encoded (e.g., `CN%3Dserver2` for `CN=server2`).
493
+
494
+ ### 6. Enable auto-sync
495
+
496
+ Add `--auto-sync` via the `command` key in each server's `docker-compose.override.yml`:
497
+
498
+ ```yaml
499
+ services:
500
+ mindoodb:
501
+ command: ["--auto-sync"]
502
+ ```
503
+
504
+ Then restart:
505
+
506
+ ```bash
507
+ docker compose up -d
508
+ ```
509
+
510
+ The servers will periodically sync all configured tenant databases, relaying encrypted entries without decrypting them. Sync config changes take effect on the next server restart or when auto-sync timers are restarted.
511
+
512
+ ## Data Directory Layout
513
+
514
+ ```
515
+ data/
516
+ ├── server.identity.json # Server keypair (Ed25519 + RSA, password-encrypted)
517
+ ├── config.json # Admin access rules (capabilities) — see "How config.json controls admin access"
518
+ ├── trusted-servers.json # Public keys of remote servers trusted for sync
519
+ ├── system-admin-*.identity.json # System admin keypair (password-encrypted)
520
+ ├── acme/ # Tenant "acme"
521
+ │ ├── config.json # Tenant config (admin keys, users, sync servers)
522
+ │ └── stores/ # Content-addressed entry stores
523
+ └── other-tenant/
524
+ ├── config.json
525
+ └── stores/
526
+ ```
527
+
528
+ ## Configuration File Formats
529
+
530
+ ### `config.json` (global — capabilities)
531
+
532
+ Controls which system admins can call which `/system/*` endpoints. See [How `config.json` controls admin access](#how-configjson-controls-admin-access) for a full explanation of the rule format and wildcard patterns.
533
+
534
+ ```json
535
+ {
536
+ "capabilities": {
537
+ "ALL:/system/*": [
538
+ {
539
+ "username": "cn=sysadmin/o=myorg",
540
+ "publicsignkey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
541
+ }
542
+ ],
543
+ "POST:/system/tenants/*": [
544
+ {
545
+ "username": "cn=provisioner/o=myorg",
546
+ "publicsignkey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
547
+ }
548
+ ]
549
+ }
550
+ }
551
+ ```
552
+
553
+ ### `server.identity.json` (global)
554
+
555
+ Generated by `npm run server:init`. Contains a `PrivateUserId` with encrypted private keys:
556
+
557
+ ```json
558
+ {
559
+ "username": "CN=server1",
560
+ "userSigningKeyPair": {
561
+ "publicKey": "-----BEGIN PUBLIC KEY-----\n...",
562
+ "privateKey": {
563
+ "ciphertext": "...",
564
+ "iv": "...",
565
+ "tag": "...",
566
+ "salt": "...",
567
+ "iterations": 100000
568
+ }
569
+ },
570
+ "userEncryptionKeyPair": {
571
+ "publicKey": "-----BEGIN PUBLIC KEY-----\n...",
572
+ "privateKey": {
573
+ "ciphertext": "...",
574
+ "iv": "...",
575
+ "tag": "...",
576
+ "salt": "...",
577
+ "iterations": 100000
578
+ }
579
+ }
580
+ }
581
+ ```
582
+
583
+ ### `trusted-servers.json` (global)
584
+
585
+ ```json
586
+ [
587
+ {
588
+ "name": "CN=server2",
589
+ "signingPublicKey": "-----BEGIN PUBLIC KEY-----\n...",
590
+ "encryptionPublicKey": "-----BEGIN PUBLIC KEY-----\n..."
591
+ }
592
+ ]
593
+ ```
594
+
595
+ ### `<tenantId>/config.json`
596
+
597
+ ```json
598
+ {
599
+ "adminUsername": "cn=admin/o=acme",
600
+ "adminSigningPublicKey": "-----BEGIN PUBLIC KEY-----\n...",
601
+ "adminEncryptionPublicKey": "-----BEGIN PUBLIC KEY-----\n...",
602
+ "publicInfosKey": "base64-encoded-aes-key",
603
+ "defaultStoreType": "file",
604
+ "users": [
605
+ {
606
+ "username": "alice",
607
+ "signingPublicKey": "-----BEGIN PUBLIC KEY-----\n...",
608
+ "encryptionPublicKey": "-----BEGIN PUBLIC KEY-----\n..."
609
+ }
610
+ ],
611
+ "remoteServers": [
612
+ {
613
+ "name": "CN=server2",
614
+ "url": "https://server2.example.com",
615
+ "syncIntervalMs": 60000,
616
+ "databases": ["directory", "main"]
617
+ }
618
+ ]
619
+ }
620
+ ```
621
+
622
+ ## Environment Variables
623
+
624
+ | Variable | Required | Description |
625
+ |----------|----------|-------------|
626
+ | `MINDOODB_SERVER_PASSWORD` | If `server.identity.json` exists and `MINDOODB_SERVER_PASSWORD_FILE` is unset | Password to decrypt server identity and per-tenant keybags |
627
+ | `MINDOODB_SERVER_PASSWORD_FILE` | No | Path to a file whose contents are the server password (trimmed). If set, **used instead of** `MINDOODB_SERVER_PASSWORD`. Prefer for Docker: env only holds the path, not the secret. |
628
+ | `MINDOODB_SYSTEM_ADMIN_PASSWORD` | No | For `server:add-to-network`: password for `--identity` (if not using `--password-file` or interactive prompt). May be visible in `ps` on shared hosts. |
629
+ | `MINDOODB_CORS_ORIGIN` | No | Allowed CORS origin (e.g., `https://app.example.com`). If not set, CORS is disabled. |
630
+ | `MINDOODB_ADMIN_ALLOWED_IPS` | No | Optional comma-separated client IPs/CIDRs allowed to call **`/system/*`** (all system admin routes, including `/system/auth/*`). If unset or `*`, any source IP may reach `/system/*` (JWT + `config.json` capabilities still apply). Example: `127.0.0.1,::1,172.23.248.0/24,2001:db8::/32`. Behind a reverse proxy, configure Express `trust proxy` so `req.ip` is the real client. |
631
+
632
+ > **Note:** The old `MINDOODB_ADMIN_API_KEY` variable has been removed. System admin **authorization** is enforced by `config.json` capabilities and JWTs. **`MINDOODB_ADMIN_ALLOWED_IPS`** is an optional **network** layer for `/system/*` only. See [Server Security](docs/server-security.md).
633
+
634
+ ## CLI Reference
635
+
636
+ ### `npm run server:dev` / `npm run server:start` — Start the server
637
+
638
+ Launches the MindooDB server process. `server:dev` runs via `ts-node` for development; `server:start` runs the compiled JavaScript. The server loads its identity from `server.identity.json` and the capabilities-based authorization config from `config.json` in the data directory.
639
+
640
+ | Option | Alias | Description | Default |
641
+ |--------|-------|-------------|---------|
642
+ | `--data-dir` | `-d` | Data directory path | `./data` |
643
+ | `--port` | `-p` | Server port | `1661` |
644
+ | `--auto-sync` | `-s` | Enable automatic sync with remote servers | disabled |
645
+ | `--static-dir` | `-w` | Serve static files at `/statics/` (e.g. bootstrap UI) | — |
646
+ | `--tls-cert` | — | Path to TLS certificate file (PEM) | — |
647
+ | `--tls-key` | — | Path to TLS private key file (PEM) | — |
648
+ | `--help` | `-h` | Show help message | — |
649
+
650
+ ### `npm run server:init` — Initialize server identity
651
+
652
+ One-time setup that generates the server's Ed25519/RSA keypair (`server.identity.json`) and an empty `trusted-servers.json`. It also interactively offers to create a first system admin keypair and writes the initial `config.json` with that admin's public key in the `capabilities` section.
653
+
654
+ | Option | Alias | Description | Default |
655
+ |--------|-------|-------------|---------|
656
+ | `--name` | `-n` | Server name (e.g., "server1") | **required** |
657
+ | `--data-dir` | `-d` | Data directory path | `./data` |
658
+ | `--force` | `-f` | Overwrite existing identity | — |
659
+ | `--help` | `-h` | Show help message | — |
660
+
661
+ ### `npm run server:add-to-network` — Add a server to the network
662
+
663
+ Automates mutual trust exchange when adding a new server to an existing network. For each existing server it fetches public keys from `/.well-known/mindoodb-server-info` and calls `POST /system/trusted-servers` in both directions so that the new server and every existing server trust each other.
664
+
665
+ | Option | Description | Default |
666
+ |--------|-------------|---------|
667
+ | `--new-server` | URL of the server being added | **required** |
668
+ | `--servers` | Comma-separated URLs of existing servers | **required** |
669
+ | `--identity` | Path to system admin `*.identity.json` (`PrivateUserId`) | **required** |
670
+ | `--password-file` | File containing the system admin password (optional if env or TTY) | — |
671
+ | `--help` | Show help message | — |
672
+
673
+ Password resolution order: read `MINDOODB_SYSTEM_ADMIN_PASSWORD`, then `--password-file`, then a hidden prompt on an interactive TTY. The CLI authenticates with the `--identity` file against every server it contacts (`--new-server` **and** each URL in `--servers`), so that identity's principal (username + public signing key) must be listed in the `capabilities` section of each server's `config.json`.
674
+
675
+ ### `npm run identity:info` — Show public information from an identity file
676
+
677
+ | Option | Description | Default |
678
+ |--------|-------------|---------|
679
+ | `--identity` | Path to an `*.identity.json` file | **required** |
680
+ | `--help` | Show help message | — |
681
+
682
+ Prints the username, a stable SHA-256 username hash (`Public user ID (hex)`), both public keys, the fixed salt string names (`signing`, `encryption`), and whether encrypted private keys are present.
683
+
684
+ ### `npm run identity:change-password` — Re-encrypt an identity with a new password
685
+
686
+ | Option | Description | Default |
687
+ |--------|-------------|---------|
688
+ | `--identity` | Path to an `*.identity.json` file | **required** |
689
+ | `--help` | Show help message | — |
690
+
691
+ Prompts for the current password, the new password, and confirmation using hidden input. The file is updated atomically in place.
692
+
693
+ ### `npm run identity:export-public` — Export only the public portion of an identity
694
+
695
+ | Option | Description | Default |
696
+ |--------|-------------|---------|
697
+ | `--identity` | Path to an `*.identity.json` file | **required** |
698
+ | `--output` | Write JSON to a file instead of stdout | stdout |
699
+ | `--help` | Show help message | — |
700
+
701
+ This emits the corresponding `PublicUserId` JSON object:
702
+
703
+ ```json
704
+ {
705
+ "username": "cn=sysadmin/o=myorg",
706
+ "userSigningPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
707
+ "userEncryptionPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
708
+ }
709
+ ```
710
+
711
+ ## API Reference
712
+
713
+ ### System Admin Endpoints (`/system/*`)
714
+
715
+ All system admin endpoints require a JWT obtained via challenge/response authentication.
716
+ See [Server Security](docs/server-security.md) for full details.
717
+
718
+ The **`curl`** examples in this document use **`$SYSTEM_ADMIN_JWT`** as a placeholder for that token. In Node you can use **`MindooDBServerAdmin`** (with your `CryptoAdapter`) instead of raw `curl`.
719
+
720
+ #### Authentication
721
+
722
+ | Method | Endpoint | Auth | Description |
723
+ |--------|----------|------|-------------|
724
+ | `POST` | `/system/auth/challenge` | None | Request a challenge (body: `{ username, publicsignkey }`) |
725
+ | `POST` | `/system/auth/authenticate` | None | Submit signed challenge |
726
+
727
+ #### Tenant Management
728
+
729
+ | Method | Endpoint | Auth | Description |
730
+ |--------|----------|------|-------------|
731
+ | `POST` | `/system/tenants/:tenantId` | JWT | Create a tenant |
732
+ | `GET` | `/system/tenants` | JWT | List all registered tenants |
733
+ | `PUT` | `/system/tenants/:tenantId` | JWT | Update tenant config |
734
+ | `DELETE` | `/system/tenants/:tenantId` | JWT | Remove a tenant |
735
+
736
+ #### Trusted Server Management
737
+
738
+ | Method | Endpoint | Auth | Description |
739
+ |--------|----------|------|-------------|
740
+ | `GET` | `/system/trusted-servers` | JWT | List trusted servers |
741
+ | `POST` | `/system/trusted-servers` | JWT | Add a trusted server |
742
+ | `DELETE` | `/system/trusted-servers/:serverName` | JWT | Remove a trusted server |
743
+
744
+ #### Per-Tenant Sync Server Management
745
+
746
+ | Method | Endpoint | Auth | Description |
747
+ |--------|----------|------|-------------|
748
+ | `GET` | `/system/tenants/:tenantId/sync-servers` | JWT | List sync servers for a tenant |
749
+ | `POST` | `/system/tenants/:tenantId/sync-servers` | JWT | Add or update a sync server |
750
+ | `DELETE` | `/system/tenants/:tenantId/sync-servers/:serverName` | JWT | Remove a sync server |
751
+ | `POST` | `/system/tenants/:tenantId/trigger-sync` | JWT | Trigger sync for a tenant |
752
+
753
+ #### Server Config Management
754
+
755
+ | Method | Endpoint | Auth | Description |
756
+ |--------|----------|------|-------------|
757
+ | `GET` | `/system/config` | JWT | Read the current server config |
758
+ | `PUT` | `/system/config` | JWT | Replace the server config (no restart needed) |
759
+
760
+ ### Authentication Endpoints
761
+
762
+ | Method | Endpoint | Description |
763
+ |--------|----------|-------------|
764
+ | `POST` | `/:tenantId/auth/challenge` | Request authentication challenge |
765
+ | `POST` | `/:tenantId/auth/authenticate` | Authenticate with signed challenge |
766
+
767
+ ### Sync Endpoints (requires JWT token)
768
+
769
+ | Method | Endpoint | Description |
770
+ |--------|----------|-------------|
771
+ | `POST` | `/:tenantId/sync/findNewEntries` | Find entries we don't have |
772
+ | `POST` | `/:tenantId/sync/findNewEntriesForDoc` | Find entries for specific document |
773
+ | `POST` | `/:tenantId/sync/getEntries` | Get specific entries |
774
+ | `POST` | `/:tenantId/sync/putEntries` | Push entries to server |
775
+ | `POST` | `/:tenantId/sync/hasEntries` | Check which entry IDs exist |
776
+ | `GET` | `/:tenantId/sync/getAllIds` | Get all entry IDs |
777
+ | `POST` | `/:tenantId/sync/resolveDependencies` | Resolve dependency chain |
778
+ | `POST` | `/:tenantId/sync/findEntries` | Find entries by type/date |
779
+ | `POST` | `/:tenantId/sync/scanEntriesSince` | Cursor-based entry scanning |
780
+ | `POST` | `/:tenantId/sync/getIdBloomSummary` | Get Bloom filter summary |
781
+ | `GET` | `/:tenantId/sync/capabilities` | Get server capabilities |
782
+
783
+ ### Server Info & Health
784
+
785
+ | Method | Endpoint | Description |
786
+ |--------|----------|-------------|
787
+ | `GET` | `/health` | Server health status |
788
+ | `GET` | `/.well-known/mindoodb-server-info` | Server name and public keys (unauthenticated) |
789
+
790
+ ## Security
791
+
792
+ The server includes the following hardening measures:
793
+
794
+ - **Input validation** -- all identifiers (tenantId, dbId, serverName) are validated to prevent path traversal. Only lowercase alphanumeric characters and hyphens are allowed, max 64 characters. Tenant IDs that collide with server route prefixes (`admin`, `health`, `statics`) are rejected as reserved names.
795
+ - **Rate limiting** -- tiered per-IP rate limits: auth endpoints (20/min), admin endpoints (30/min), sync endpoints (200/min), global fallback (500/min). Returns `429 Too Many Requests` when exceeded.
796
+ - **Security headers** -- `helmet` middleware sets X-Content-Type-Options, X-Frame-Options, Content-Security-Policy, Strict-Transport-Security, and others.
797
+ - **CORS** -- disabled by default. Set `MINDOODB_CORS_ORIGIN` to allow a specific origin.
798
+ - **Constant-time key comparison** -- tenant creation API keys are compared using `crypto.timingSafeEqual()` where applicable.
799
+ - **Error sanitization** -- internal errors never leak file paths or stack traces to clients. Only known auth/validation errors return specific messages.
800
+ - **Request size limits** -- JSON body limited to 5MB. Array sizes capped (100k IDs, 10k entries for putEntries).
801
+ - **Connection timeouts** -- idle connections are closed after 30 seconds.
802
+ - **Optional `/system/*` IP allowlist** — set `MINDOODB_ADMIN_ALLOWED_IPS` to a comma-separated list of IPs or IPv4/IPv6 CIDRs (e.g., `127.0.0.1,::1,10.0.0.0/8,2001:db8::/32`) to restrict which client addresses may call **any** `/system/*` route (including auth). If unset or `*`, there is no IP restriction at this layer. IPv4-mapped IPv6 (`::ffff:127.0.0.1`) is normalized. Behind a reverse proxy, configure Express `trust proxy` so `req.ip` is accurate.
803
+
804
+ For production deployments, also consider:
805
+
806
+ - Enabling TLS (see below) or running behind a reverse proxy (nginx, Caddy) with TLS termination
807
+ - Setting `MINDOODB_ADMIN_ALLOWED_IPS` if `/system/*` should only be reachable from operator networks
808
+ - Using a process manager (PM2, systemd) for automatic restarts
809
+
810
+ ## Static File Serving
811
+
812
+ The server can serve static files from a local directory, which is useful for hosting a bootstrap UI for [distributed web applications](docs/distributed-webapps.md). When `--static-dir` is provided, two additional routes are available:
813
+
814
+ | Method | Endpoint | Description |
815
+ |--------|----------|-------------|
816
+ | `GET` | `/` | Redirects to `/statics/index.html` if the file exists (302), otherwise returns 404 |
817
+ | `GET` | `/statics/*` | Serves files from the configured static directory |
818
+
819
+ ### Usage
820
+
821
+ ```bash
822
+ MINDOODB_SERVER_PASSWORD_FILE=./.server-password npm run server:dev -- --static-dir ./webapp-bootstrap
823
+ ```
824
+
825
+ The static directory might contain a minimal bootstrap page that registers a service worker and triggers the initial MindooDB sync for a distributed web application:
826
+
827
+ ```
828
+ webapp-bootstrap/
829
+ ├── index.html # Minimal HTML shell, registers the service worker
830
+ ├── bootstrap.js # Sync logic: check pointer DB, sync UI DB
831
+ ├── sw.js # Service worker: serves UI assets from IndexedDB
832
+ └── style.css # Loading indicator styles
833
+ ```
834
+
835
+ Path traversal is prevented by an explicit guard that rejects requests containing `..`, in addition to the built-in protection provided by Express's static middleware. Dotfiles (`.env`, `.git`, etc.) are not served.
836
+
837
+ ## TLS / HTTPS
838
+
839
+ The server supports TLS directly via `--tls-cert` and `--tls-key` flags. No additional dependencies are required.
840
+
841
+ ### Starting with TLS
842
+
843
+ ```bash
844
+ MINDOODB_SERVER_PASSWORD_FILE=./.server-password npm run server:dev -- \
845
+ --tls-cert /etc/letsencrypt/live/sync.example.com/fullchain.pem \
846
+ --tls-key /etc/letsencrypt/live/sync.example.com/privkey.pem \
847
+ -p 443
848
+ ```
849
+
850
+ Both flags must be provided together. The certificate file should be the full chain (PEM format).
851
+
852
+ ### Free certificates with Let's Encrypt
853
+
854
+ #### Method A: Standalone (HTTP-01)
855
+
856
+ Certbot briefly binds port 80 to prove domain ownership. Best when port 80 is available.
857
+
858
+ ```bash
859
+ # Install certbot (Ubuntu/Debian)
860
+ sudo apt install certbot
861
+
862
+ # Obtain certificate
863
+ sudo certbot certonly --standalone -d sync.example.com
864
+ ```
865
+
866
+ Auto-renewal is handled by certbot's systemd timer, which runs automatically on most Linux distributions. After renewal, restart the server to pick up the new certificate.
867
+
868
+ #### Method B: DNS (DNS-01)
869
+
870
+ Prove ownership via a DNS TXT record. No port 80 required -- works behind firewalls and on non-standard ports.
871
+
872
+ **Automated (recommended for production)** -- use a DNS provider plugin so renewal is fully unattended:
873
+
874
+ ```bash
875
+ # Example with Cloudflare
876
+ sudo apt install certbot python3-certbot-dns-cloudflare
877
+
878
+ # Create credentials file
879
+ cat > /etc/letsencrypt/cloudflare.ini << EOF
880
+ dns_cloudflare_api_token = your-cloudflare-api-token
881
+ EOF
882
+ chmod 600 /etc/letsencrypt/cloudflare.ini
883
+
884
+ # Obtain certificate
885
+ sudo certbot certonly --dns-cloudflare \
886
+ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
887
+ -d sync.example.com
888
+ ```
889
+
890
+ With a DNS plugin, certbot creates and removes the TXT record via the provider's API automatically. The systemd timer handles renewal with zero manual intervention.
891
+
892
+ **Manual (testing only)** -- requires updating the DNS TXT record by hand every 90 days:
893
+
894
+ ```bash
895
+ sudo certbot certonly --manual --preferred-challenges dns -d sync.example.com
896
+ # Certbot will ask you to create: _acme-challenge.sync.example.com TXT "..."
897
+ ```
898
+
899
+ Available DNS plugins include Cloudflare, Route53, Google Cloud DNS, DigitalOcean, Linode, and OVH. See the [certbot documentation](https://eff-certbot.readthedocs.io/en/latest/using.html#dns-plugins) for the full list.
900
+
901
+ ## Docker
902
+
903
+ The server ships with a multi-stage `Dockerfile` under `src/node/server/` that produces a minimal Alpine-based image. The build context must be the **repository root** so the library and server compile together.
904
+
905
+ **Recommended:** use `bash serversetup.sh` (see [Quick Start](#quick-start)) to build, initialise, and configure everything interactively. The rest of this section covers manual Docker commands for advanced use cases.
906
+
907
+ ### docker-compose.yml
908
+
909
+ The repository includes a `docker-compose.yml` that uses the default data paths (`../mindoodb-data`). `serversetup.sh` writes a `docker-compose.override.yml` that docker compose merges automatically. It pins the container to your current host uid/gid so the non-root container can read the password file and write to the mounted data directory. On SELinux hosts it also adds the required mount suffixes.
910
+
911
+ The generated override also contains the published port bindings. This supports all three common setups:
912
+ - bind all interfaces (`0.0.0.0`)
913
+ - bind a single specific IP (for example a VPN address)
914
+ - bind both `127.0.0.1` and one specific extra IP
915
+
916
+ ### Manual Docker commands (without serversetup.sh)
917
+
918
+ If you prefer not to use the setup script, here are the individual steps:
919
+
920
+ ```bash
921
+ # Build the image
922
+ docker build -f src/node/server/Dockerfile -t mindoodb-server .
923
+
924
+ # Create data directory and password file
925
+ mkdir -p ../mindoodb-data/server
926
+ printf '%s' 'your-secret' > ../mindoodb-data/.server_unlock
927
+ chmod 600 ../mindoodb-data/.server_unlock
928
+
929
+ # Initialize server identity (interactive — prompts for system admin creation)
930
+ # Run as your current host uid/gid so the container can read/write the bind mounts.
931
+ docker run --rm -it \
932
+ --user "$(id -u):$(id -g)" \
933
+ -v "$(pwd)/../mindoodb-data/server:/data" \
934
+ -v "$(pwd)/../mindoodb-data/.server_unlock:/run/secrets/server_unlock:ro" \
935
+ -e MINDOODB_SERVER_PASSWORD_FILE=/run/secrets/server_unlock \
936
+ --entrypoint node \
937
+ mindoodb-server dist/node/server/serverinit.js --data-dir /data --name server1
938
+
939
+ # Start the server
940
+ docker compose up -d
941
+
942
+ # Verify
943
+ curl http://localhost:1661/health
944
+ ```
945
+
946
+ On SELinux hosts, append `:Z` to the `/data` bind mount and `,Z` to the read-only password-file mount.
947
+
948
+ ### Bind to a specific IP
949
+
950
+ To restrict the server to a specific network interface (e.g. a VPN), rerun `bash serversetup.sh` and provide the bind address when prompted.
951
+
952
+ If you also want local checks from the same host, answer `y` when asked whether to also bind `127.0.0.1`. The generated `docker-compose.override.yml` will then contain both mappings.
953
+
954
+ ### Additional flags
955
+
956
+ Environment variables and CLI flags can be added to `docker-compose.override.yml`:
957
+
958
+ ```yaml
959
+ services:
960
+ mindoodb:
961
+ environment:
962
+ MINDOODB_ADMIN_ALLOWED_IPS: "127.0.0.1,10.0.0.0/8"
963
+ command: ["--port", "8443", "--auto-sync"]
964
+ ports:
965
+ - "0.0.0.0:8443:8443"
966
+ ```
967
+
968
+ ### Password handling
969
+
970
+ Prefer **`MINDOODB_SERVER_PASSWORD_FILE`** over **`MINDOODB_SERVER_PASSWORD`** so the plaintext secret is not stored in the process environment block (visible via `docker inspect`, `/proc/<pid>/environ`). The setup script and `docker-compose.yml` use file-based passwords by default. See [Server Security](docs/server-security.md) for details on Docker secrets.
971
+
972
+ ## Development
973
+
974
+ From the **repository root** (where `README-server.md` lives):
975
+
976
+ ### Build
977
+
978
+ ```bash
979
+ npm run build
980
+ ```
981
+
982
+ ### Run in development mode
983
+
984
+ ```bash
985
+ MINDOODB_SERVER_PASSWORD_FILE=./.server-password npm run server:dev -- -d ./data -p 1661
986
+ ```
987
+
988
+ ### Run built version
989
+
990
+ ```bash
991
+ MINDOODB_SERVER_PASSWORD_FILE=./.server-password npm run server:start -- -d ./data -p 1661
992
+ ```
993
+
994
+ ## Testing
995
+
996
+ The integration tests are located in the main MindooDB package at `src/__tests__/ExampleServer.test.ts`. Run them with:
997
+
998
+ ```bash
999
+ # From the root mindoodb directory
1000
+ npm test -- ExampleServer
1001
+ ```