@underpostnet/cyberia 3.3.0 → 3.3.76

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 (568) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.example +17 -5
  3. package/.github/workflows/coverall.cyberia.ci.yml +39 -14
  4. package/.github/workflows/cyberia-client.cd.yml +5 -0
  5. package/.github/workflows/cyberia-server.cd.yml +5 -0
  6. package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
  7. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
  8. package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
  9. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
  10. package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
  11. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
  12. package/.github/workflows/engine-cyberia.cd.yml +3 -0
  13. package/.github/workflows/engine-cyberia.ci.yml +4 -5
  14. package/.github/workflows/ghpkg.ci.yml +35 -20
  15. package/.github/workflows/hardhat.ci.yml +12 -11
  16. package/.github/workflows/npmpkg.ci.yml +17 -15
  17. package/.github/workflows/publish.cyberia.ci.yml +44 -0
  18. package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
  19. package/.github/workflows/release.cd.yml +1 -0
  20. package/.prettierignore +1 -0
  21. package/AGENTS.md +96 -0
  22. package/CHANGELOG.md +653 -2513
  23. package/CLI-HELP.md +323 -157
  24. package/Dockerfile +25 -9
  25. package/Dockerfile.dev +25 -9
  26. package/Dockerfile.test +5 -5
  27. package/README.md +48 -28
  28. package/bin/build.js +86 -41
  29. package/bin/build.template.js +8 -3
  30. package/bin/cyberia.js +1494 -1403
  31. package/bin/deploy.js +18 -104
  32. package/bin/index.js +1494 -1403
  33. package/compose.env +17 -5
  34. package/conf.js +98 -7
  35. package/deploy/cyberia-client/deploy.sh +46 -0
  36. package/deploy/cyberia-client/package.sh +17 -0
  37. package/deploy/cyberia-client/state.sh +15 -0
  38. package/deploy/cyberia-server/deploy.sh +46 -0
  39. package/deploy/cyberia-server/package.sh +17 -0
  40. package/deploy/cyberia-server/state.sh +15 -0
  41. package/deploy/dd-cyberia/deploy.sh +9 -29
  42. package/deploy/dd-cyberia/init.sh +38 -45
  43. package/deploy/dd-cyberia/package.sh +16 -0
  44. package/deploy/dd-cyberia/state.sh +14 -0
  45. package/deploy/dd-cyberia/sync-deploy.sh +133 -147
  46. package/deploy/lib/config.sh +29 -0
  47. package/deploy/lib/github-actions-logging.sh +610 -0
  48. package/deploy/lib/host.sh +194 -0
  49. package/deploy/lib/state.sh +65 -0
  50. package/deploy/pwa-microservices-template/deploy.sh +17 -42
  51. package/deploy/release/deploy.sh +9 -41
  52. package/deployment.yaml +7 -2
  53. package/docker-compose.yml +23 -17
  54. package/examples/static-page/README.md +19 -19
  55. package/examples/static-page/ssr-components/CustomPage.js +2 -2
  56. package/hardhat/hardhat.config.js +1 -6
  57. package/hardhat/package-lock.json +101 -40
  58. package/hardhat/package.json +2 -2
  59. package/manifests/alertmanager/deployment.yaml +81 -0
  60. package/manifests/blackbox-exporter/deployment.yaml +75 -0
  61. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +32 -7
  62. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +32 -7
  63. package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
  64. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  65. package/manifests/deployment/mongo-express/deployment.yaml +46 -27
  66. package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
  67. package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
  68. package/manifests/deployment/mongo-express/service.yaml +16 -0
  69. package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
  70. package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
  71. package/manifests/grafana/deployment.yaml +26 -7
  72. package/manifests/grafana/kustomization.yaml +1 -0
  73. package/manifests/grafana/service.yaml +6 -1
  74. package/manifests/mariadb/pv.yaml +1 -0
  75. package/manifests/mongodb/statefulset.yaml +9 -2
  76. package/manifests/mysql/pv-pvc.yaml +14 -1
  77. package/manifests/postgresql/pv.yaml +12 -1
  78. package/manifests/postgresql/pvc.yaml +1 -0
  79. package/manifests/prometheus/deployment.yaml +18 -22
  80. package/package.json +87 -12
  81. package/scripts/event-monitor.sh +56 -0
  82. package/scripts/k3s-node-setup.sh +27 -14
  83. package/scripts/kubeadm-node-setup.sh +31 -11
  84. package/scripts/maas-setup.sh +8 -8
  85. package/scripts/selinux-normalize.sh +907 -0
  86. package/scripts/shutdown-machine.sh +843 -0
  87. package/scripts/test-monitor.sh +5 -5
  88. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +15 -10
  89. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
  90. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +7 -2
  91. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +90 -186
  92. package/src/api/core/core.controller.js +1 -1
  93. package/src/api/core/core.router.js +2 -2
  94. package/src/api/core/core.service.js +2 -2
  95. package/src/api/crypto/crypto.controller.js +1 -1
  96. package/src/api/crypto/crypto.service.js +1 -1
  97. package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
  98. package/src/api/cyberia-action/cyberia-action.router.js +3 -3
  99. package/src/api/cyberia-action/cyberia-action.service.js +5 -6
  100. package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
  101. package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
  102. package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
  103. package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
  104. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +2 -4
  105. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
  106. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +13 -35
  107. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +17 -34
  108. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
  109. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
  110. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +2 -2
  111. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
  112. package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
  113. package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
  114. package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
  115. package/src/api/cyberia-entity/cyberia-entity.service.js +3 -3
  116. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
  117. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
  118. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -2
  119. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +468 -5
  120. package/src/api/cyberia-instance/cyberia-fallback-capture.js +502 -0
  121. package/src/api/cyberia-instance/cyberia-fallback-world.js +29 -40
  122. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
  123. package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
  124. package/src/api/cyberia-instance/cyberia-instance-map.service.js +35 -142
  125. package/src/api/cyberia-instance/cyberia-instance.controller.js +2 -5
  126. package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
  127. package/src/api/cyberia-instance/cyberia-instance.router.js +5 -18
  128. package/src/api/cyberia-instance/cyberia-instance.service.js +5 -45
  129. package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
  130. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
  131. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
  132. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
  133. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +76 -3
  134. package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
  135. package/src/api/cyberia-map/cyberia-map.router.js +1 -1
  136. package/src/api/cyberia-map/cyberia-map.service.js +8 -4
  137. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
  138. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
  139. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
  140. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
  141. package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
  142. package/src/api/cyberia-quest/cyberia-quest.router.js +4 -4
  143. package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
  144. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
  145. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
  146. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
  147. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
  148. package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
  149. package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
  150. package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
  151. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -214
  152. package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
  153. package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
  154. package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
  155. package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
  156. package/src/api/default/default.controller.js +1 -1
  157. package/src/api/default/default.router.js +1 -1
  158. package/src/api/default/default.service.js +2 -2
  159. package/src/api/document/document.controller.js +1 -1
  160. package/src/api/document/document.router.js +1 -1
  161. package/src/api/document/document.service.js +2 -2
  162. package/src/api/file/file.controller.js +1 -1
  163. package/src/api/file/file.ref.js +153 -0
  164. package/src/api/file/file.ref.json +11 -2
  165. package/src/api/file/file.router.js +1 -1
  166. package/src/api/file/file.service.js +2 -2
  167. package/src/api/instance/instance.controller.js +1 -1
  168. package/src/api/instance/instance.router.js +1 -1
  169. package/src/api/instance/instance.service.js +2 -2
  170. package/src/api/ipfs/ipfs.controller.js +1 -1
  171. package/src/api/ipfs/ipfs.router.js +2 -2
  172. package/src/api/ipfs/ipfs.service.js +2 -2
  173. package/src/api/object-layer/object-layer.controller.js +1 -1
  174. package/src/api/object-layer/object-layer.model.js +47 -12
  175. package/src/api/object-layer/object-layer.router.js +12 -5
  176. package/src/api/object-layer/object-layer.service.js +116 -123
  177. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
  178. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
  179. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
  180. package/src/api/test/test.controller.js +1 -1
  181. package/src/api/test/test.service.js +1 -1
  182. package/src/api/user/guest.service.js +3 -3
  183. package/src/api/user/user.controller.js +1 -1
  184. package/src/api/user/user.router.js +2 -2
  185. package/src/api/user/user.service.js +4 -4
  186. package/src/api.js +5 -9
  187. package/src/cli/app.js +402 -0
  188. package/src/cli/baremetal.js +30 -35
  189. package/src/cli/client.js +306 -0
  190. package/src/cli/cloud-init.js +3 -3
  191. package/src/cli/cluster.js +191 -82
  192. package/src/cli/db.js +120 -68
  193. package/src/cli/deploy.js +157 -75
  194. package/src/cli/docker-compose.js +5 -5
  195. package/src/cli/domains.js +184 -0
  196. package/src/cli/dotenv-store.js +143 -0
  197. package/src/cli/event.js +2281 -0
  198. package/src/cli/fs-selection.js +115 -0
  199. package/src/cli/fs.js +159 -343
  200. package/src/cli/host.js +672 -0
  201. package/src/cli/image.js +4 -6
  202. package/src/cli/index.js +343 -151
  203. package/src/cli/ipfs.js +34 -30
  204. package/src/cli/kickstart.js +1 -1
  205. package/src/cli/kubectl.js +218 -41
  206. package/src/cli/lxd.js +3 -3
  207. package/src/cli/monitor.js +1359 -26
  208. package/src/cli/package.js +90 -0
  209. package/src/cli/release.js +16 -12
  210. package/src/cli/repository.js +586 -363
  211. package/src/cli/run.js +398 -843
  212. package/src/cli/secrets.js +2158 -964
  213. package/src/cli/ssh.js +478 -279
  214. package/src/cli/state.js +394 -0
  215. package/src/cli/static.js +2 -2
  216. package/src/cli/system.js +1 -1
  217. package/src/cli/test.js +203 -125
  218. package/src/cli/vultr.js +61 -21
  219. package/src/cli/wireguard.js +1112 -306
  220. package/src/client/components/core/CalendarCore.js +53 -96
  221. package/src/client/components/core/Docs.js +327 -303
  222. package/src/client/components/core/FileExplorer.js +0 -21
  223. package/src/client/components/core/Modal.js +56 -1
  224. package/src/client/components/core/PanelForm.js +4 -4
  225. package/src/client/components/core/Repository.js +56 -0
  226. package/src/client/components/core/Translate.js +7 -0
  227. package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
  228. package/src/client/components/core/Wallet.js +0 -11
  229. package/src/client/components/core/Worker.js +7 -1
  230. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
  231. package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
  232. package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
  233. package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
  234. package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
  235. package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
  236. package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
  237. package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
  238. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
  239. package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
  240. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +2 -35
  241. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
  242. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
  243. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
  244. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
  245. package/src/client/components/default/AppShellDefault.js +1 -1
  246. package/src/client/components/default/SettingsDefault.js +2 -2
  247. package/src/client/components/itemledger/SettingsItemledger.js +2 -2
  248. package/src/client/components/underpost/AppShellUnderpost.js +3 -2
  249. package/src/client/components/underpost/SettingsUnderpost.js +2 -2
  250. package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
  251. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +313 -30
  252. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
  253. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
  254. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
  255. package/src/client/public/cyberia-docs/CYBERIA.md +48 -28
  256. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
  257. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
  258. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  259. package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
  260. package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
  261. package/src/client/public/default/android-chrome-144x144.png +0 -0
  262. package/src/client/public/default/android-chrome-192x192.png +0 -0
  263. package/src/client/public/default/android-chrome-256x256.png +0 -0
  264. package/src/client/public/default/android-chrome-36x36.png +0 -0
  265. package/src/client/public/default/android-chrome-384x384.png +0 -0
  266. package/src/client/public/default/android-chrome-48x48.png +0 -0
  267. package/src/client/public/default/android-chrome-512x512.png +0 -0
  268. package/src/client/public/default/android-chrome-72x72.png +0 -0
  269. package/src/client/public/default/android-chrome-96x96.png +0 -0
  270. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  271. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  272. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  273. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  274. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  275. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  276. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  277. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  278. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  279. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  280. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  281. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  282. package/src/client/public/default/apple-touch-icon.png +0 -0
  283. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  284. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  285. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  286. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  287. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  288. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  289. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  290. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  291. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  292. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  293. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  294. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  295. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  296. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  297. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  298. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  299. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  300. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  301. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  302. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  303. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  304. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  305. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  306. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  307. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  308. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  309. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  310. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  311. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  312. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  313. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  314. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  315. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  316. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  317. package/src/client/public/default/assets/background/white.jpg +0 -0
  318. package/src/client/public/default/browserconfig.xml +1 -1
  319. package/src/client/public/default/favicon-16x16.png +0 -0
  320. package/src/client/public/default/favicon-32x32.png +0 -0
  321. package/src/client/public/default/favicon-48x48.png +0 -0
  322. package/src/client/public/default/favicon.ico +0 -0
  323. package/src/client/public/default/manifest.webmanifest +5 -5
  324. package/src/client/public/default/mstile-144x144.png +0 -0
  325. package/src/client/public/default/mstile-150x150.png +0 -0
  326. package/src/client/public/default/mstile-310x150.png +0 -0
  327. package/src/client/public/default/mstile-310x310.png +0 -0
  328. package/src/client/public/default/mstile-70x70.png +0 -0
  329. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  330. package/src/client/public/default/yandex-browser-manifest.json +1 -1
  331. package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
  332. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
  333. package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
  334. package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
  335. package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
  336. package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
  337. package/src/client/services/object-layer/object-layer.management.js +6 -10
  338. package/src/client/services/object-layer/object-layer.service.js +3 -1
  339. package/src/client/ssr/RootDocument.js +1 -1
  340. package/src/client/ssr/body/404.js +1 -1
  341. package/src/client/ssr/body/500.js +1 -1
  342. package/src/client/ssr/body/CacheControl.js +1 -1
  343. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
  344. package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
  345. package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
  346. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
  347. package/src/client/ssr/head/CryptokoynScripts.js +1 -1
  348. package/src/client/ssr/head/Css.js +1 -1
  349. package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
  350. package/src/client/ssr/head/DefaultScripts.js +1 -1
  351. package/src/client/ssr/head/ItemledgerScripts.js +1 -1
  352. package/src/client/ssr/head/Microdata.js +1 -1
  353. package/src/client/ssr/head/Production.js +1 -1
  354. package/src/client/ssr/head/Pwa.js +1 -1
  355. package/src/client/ssr/head/PwaItemledger.js +1 -1
  356. package/src/client/ssr/head/Seo.js +1 -1
  357. package/src/client/ssr/head/UnderpostScripts.js +1 -1
  358. package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
  359. package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
  360. package/src/client/ssr/views/Cyberia404.js +7 -5
  361. package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
  362. package/src/client/ssr/views/Maintenance.js +1 -1
  363. package/src/client/ssr/views/NoNetworkConnection.js +1 -1
  364. package/src/client/ssr/views/Test.js +1 -1
  365. package/src/client/sw/core.sw.js +20 -14
  366. package/src/client-builder/client-build-docs.js +92 -77
  367. package/src/client-builder/client-build-live.js +2 -2
  368. package/src/client-builder/client-build.js +105 -74
  369. package/src/client-builder/client-bundle.js +347 -0
  370. package/src/client-builder/client-dev-server.js +3 -3
  371. package/src/client-builder/client-icons.js +2 -2
  372. package/src/client-builder/ssr.js +3 -3
  373. package/src/client.build.js +3 -6
  374. package/src/client.dev.js +3 -6
  375. package/src/db/DataBaseProvider.js +186 -3
  376. package/src/db/mariadb/MariaDB.js +3 -2
  377. package/src/db/mongo/MongoBootstrap.js +273 -101
  378. package/src/db/mongo/MongoExpress.js +187 -0
  379. package/src/db/mongo/MongooseDB.js +7 -8
  380. package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
  381. package/src/grpc/cyberia/grpc-server.js +6 -8
  382. package/src/index.js +79 -23
  383. package/src/mailer/EmailRender.js +2 -2
  384. package/src/mailer/MailerInterceptor.js +87 -0
  385. package/src/mailer/MailerProvider.js +50 -6
  386. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +144 -147
  387. package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
  388. package/src/projects/cyberia/besu-genesis-generator.js +8 -17
  389. package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
  390. package/src/projects/cyberia/catalog-cyberia.js +54 -10
  391. package/src/projects/cyberia/gemini-client.js +4 -4
  392. package/src/projects/cyberia/generate-saga.js +5 -12
  393. package/src/projects/cyberia/hot-reload-trigger.js +18 -29
  394. package/src/projects/cyberia/instance-backup.js +210 -0
  395. package/src/projects/cyberia/instance-data.js +178 -258
  396. package/src/projects/cyberia/ipfs-client.js +4 -4
  397. package/src/projects/cyberia/map-preview-generator.js +33 -69
  398. package/src/projects/cyberia/object-layer.js +153 -320
  399. package/src/projects/cyberia/seed-audio.js +139 -0
  400. package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
  401. package/src/projects/cyberia/semantic-layer-generator.js +3 -19
  402. package/src/projects/cyberia/stat-balance.js +81 -0
  403. package/src/projects/cyberia/stat-commands.js +23 -0
  404. package/src/projects/cyberia/stat-contract-generator.js +101 -0
  405. package/src/projects/underpost/catalog-underpost.js +9 -1
  406. package/src/proxy.js +4 -7
  407. package/src/runtime/cyberia-client/Dockerfile +17 -8
  408. package/src/runtime/cyberia-client/Dockerfile.dev +13 -4
  409. package/src/runtime/cyberia-server/Dockerfile +18 -6
  410. package/src/runtime/cyberia-server/Dockerfile.dev +16 -3
  411. package/src/runtime/engine-cyberia/Dockerfile +25 -9
  412. package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
  413. package/src/runtime/engine-cyberia/Dockerfile.test +5 -5
  414. package/src/runtime/engine-cyberia/compose.env +17 -5
  415. package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
  416. package/src/runtime/express/Express.js +32 -27
  417. package/src/runtime/lampp/Lampp.js +22 -70
  418. package/src/runtime/nginx/Nginx.js +1 -1
  419. package/src/runtime/wp/Wp.js +16 -48
  420. package/src/server/{catalog.js → build/catalog.js} +15 -5
  421. package/src/server/build/coverage.js +188 -0
  422. package/src/server/build/execution.js +402 -0
  423. package/src/server/build/package.js +508 -0
  424. package/src/server/build/testing.js +578 -0
  425. package/src/server/{dns.js → network/dns.js} +144 -11
  426. package/src/server/{forward-proxy.js → network/forward-proxy.js} +10 -18
  427. package/src/server/{middlewares.js → network/middlewares.js} +11 -5
  428. package/src/server/network/node-capability.js +98 -0
  429. package/src/server/{peer.js → network/peer.js} +3 -3
  430. package/src/server/{proxy.js → network/proxy.js} +7 -13
  431. package/src/server/network/router.js +356 -0
  432. package/src/server/{tls.js → network/tls.js} +2 -2
  433. package/src/server/{underpost-compression.js → network/underpost-compression.js} +1 -1
  434. package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +7 -7
  435. package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +2 -2
  436. package/src/server/{cri.js → ops/cri.js} +2 -2
  437. package/src/server/{cron.js → ops/cron.js} +349 -74
  438. package/src/server/ops/event-notification.js +284 -0
  439. package/src/server/{logger.js → ops/logger.js} +143 -23
  440. package/src/server/ops/monitoring.js +1724 -0
  441. package/src/server/{systemd.js → ops/systemd.js} +55 -1
  442. package/src/server/{conf.js → runtime/conf.js} +516 -423
  443. package/src/server/runtime/config-scope.js +215 -0
  444. package/src/server/runtime/environment.js +149 -0
  445. package/src/server/{process.js → runtime/process.js} +153 -12
  446. package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
  447. package/src/server/{runtime.js → runtime/runtime.js} +12 -8
  448. package/src/server/{start.js → runtime/start.js} +128 -39
  449. package/src/server/{auth.js → security/auth.js} +7 -8
  450. package/src/server/security/container-storage.js +382 -0
  451. package/src/server/{crypto.js → security/crypto.js} +1 -1
  452. package/src/server/{selinux.js → security/selinux.js} +1 -1
  453. package/src/server/{backup.js → storage/backup.js} +17 -8
  454. package/src/server/{data-query.js → storage/data-query.js} +17 -1
  455. package/src/server/storage/downloader.js +121 -0
  456. package/src/server/storage/repository.js +68 -0
  457. package/src/server/storage/zip.js +153 -0
  458. package/src/server.js +4 -7
  459. package/src/ws/IoInterface.js +1 -1
  460. package/src/ws/IoServer.js +1 -1
  461. package/src/ws/core/core.ws.connection.js +1 -1
  462. package/src/ws/core/core.ws.emit.js +1 -1
  463. package/src/ws/core/core.ws.server.js +1 -1
  464. package/src/ws/default/default.ws.connection.js +1 -1
  465. package/src/ws/default/default.ws.emit.js +1 -1
  466. package/src/ws/default/default.ws.server.js +1 -1
  467. package/test/e2e/event-e2e-public-ingress-down.js +45 -0
  468. package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
  469. package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
  470. package/test/{api.test.js → integration/app/api.test.js} +33 -14
  471. package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
  472. package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
  473. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +64 -0
  474. package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
  475. package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
  476. package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +17 -23
  477. package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
  478. package/test/integration/app/cyberia/cyberia-load.test.js +466 -0
  479. package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
  480. package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
  481. package/test/integration/app/cyberia/fallback-world-capture.test.js +338 -0
  482. package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
  483. package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
  484. package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
  485. package/test/integration/app/cyberia/seed-audio.test.js +275 -0
  486. package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
  487. package/test/integration/infra/1-security/config-scope.test.js +305 -0
  488. package/test/integration/infra/1-security/container-storage.test.js +356 -0
  489. package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
  490. package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
  491. package/test/{selinux.test.js → integration/infra/1-security/selinux.test.js} +2 -4
  492. package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
  493. package/test/integration/infra/1-security/systemd-service.test.js +70 -0
  494. package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
  495. package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
  496. package/test/integration/infra/2-network/node-capability.test.js +182 -0
  497. package/test/integration/infra/2-network/wireguard-cli.test.js +1187 -0
  498. package/test/{wireguard-edge.test.js → integration/infra/2-network/wireguard-edge.test.js} +649 -55
  499. package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
  500. package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
  501. package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
  502. package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
  503. package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
  504. package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
  505. package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
  506. package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
  507. package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +6 -6
  508. package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +4 -4
  509. package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
  510. package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
  511. package/test/integration/infra/5-observability/event-notification.test.js +197 -0
  512. package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
  513. package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
  514. package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
  515. package/test/support/shell-harness.js +73 -0
  516. package/test/unit/build-template.test.js +97 -0
  517. package/test/unit/build-workflow-offline.test.js +132 -0
  518. package/test/unit/catalog.test.js +71 -0
  519. package/test/unit/client-build-docs.test.js +361 -0
  520. package/test/unit/client-bundle.test.js +313 -0
  521. package/test/unit/conf-loading.test.js +303 -0
  522. package/test/unit/conf-resolution.test.js +996 -0
  523. package/test/unit/coverage-artifact.test.js +221 -0
  524. package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
  525. package/test/unit/cyberia/instance-backup.test.js +183 -0
  526. package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
  527. package/test/unit/cyberia/publish-workflow.test.js +22 -0
  528. package/test/unit/cyberia/stat-balance.test.js +69 -0
  529. package/test/unit/deploy-env-resolution.test.js +38 -0
  530. package/test/unit/deploy-log-table.test.js +518 -0
  531. package/test/unit/downloader-integrity.test.js +98 -0
  532. package/test/unit/execution-profiles.test.js +219 -0
  533. package/test/unit/file-reference-registry.test.js +141 -0
  534. package/test/unit/fleet-sync-source.test.js +149 -0
  535. package/test/unit/fs-storage-paths.test.js +596 -0
  536. package/test/unit/logger-redaction.test.js +141 -0
  537. package/test/unit/package.test.js +457 -0
  538. package/test/unit/prepare-host.test.js +382 -0
  539. package/test/unit/process-environment.test.js +18 -0
  540. package/test/unit/propagation-message.test.js +78 -0
  541. package/test/unit/redeploy-plan.test.js +24 -0
  542. package/test/unit/release-bump.test.js +474 -0
  543. package/test/unit/ssh-output-redaction.test.js +28 -0
  544. package/test/unit/start-options.test.js +46 -0
  545. package/test/unit/test-tiers.test.js +220 -0
  546. package/test/unit/zip-archive.test.js +142 -0
  547. package/vitest.config.js +66 -0
  548. package/.nycrc +0 -9
  549. package/deploy/lib/logging.sh +0 -96
  550. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
  551. package/manifests/mongodb/pv-pvc.yaml +0 -59
  552. package/scripts/coverall-test-cyberia.sh +0 -24
  553. package/scripts/coverall-test.sh +0 -24
  554. package/scripts/link-local-underpost-cli.sh +0 -6
  555. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
  556. package/src/api/user/user.build.js +0 -16
  557. package/src/cli/env.js +0 -177
  558. package/src/client/components/core/Blockchain.js +0 -41
  559. package/src/client/components/core/Blog.js +0 -9
  560. package/src/client/components/core/KeyboardAvoidance.js +0 -145
  561. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
  562. package/src/client/public/default/assets/background/dark.jpg +0 -0
  563. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  564. package/src/client/public/default/assets/background/white0.jpg +0 -0
  565. package/src/client/public/doc/favicon.ico +0 -0
  566. package/src/client/public/doc/sitemap +0 -148
  567. package/src/server/downloader.js +0 -73
  568. package/src/server/environment.js +0 -98
@@ -1,6 +1,7 @@
1
1
  import { createPool } from 'mariadb';
2
2
  import Underpost from '../../index.js';
3
- import { loggerFactory } from '../../server/logger.js';
3
+ import { loggerFactory } from '../../server/ops/logger.js';
4
+ import { latchRuntimeError } from '../../server/runtime/runtime-status.js';
4
5
 
5
6
  /**
6
7
  * Module for interacting with MariaDB/MySQL databases using the mariadb connector.
@@ -51,7 +52,7 @@ class MariaDBService {
51
52
  console.log(result);
52
53
  } catch (error) {
53
54
  logger.error('MariaDB query failed', { error: error.message });
54
- if (Underpost.env.isInsideContainer()) Underpost.env.set('container-status', 'error');
55
+ latchRuntimeError();
55
56
  } finally {
56
57
  if (conn) conn.release(); // release to pool
57
58
  await pool.end();
@@ -9,15 +9,18 @@
9
9
  */
10
10
 
11
11
  import fs from 'fs-extra';
12
- import { loggerFactory } from '../../server/logger.js';
13
- import { shellExec } from '../../server/process.js';
14
- import { crictlCommandFactory } from '../../server/cri.js';
15
- import { resolveReplicaCount } from '../../server/conf.js';
12
+ import { loggerFactory } from '../../server/ops/logger.js';
13
+ import { shellExec } from '../../server/runtime/process.js';
14
+ import { crictlCommandFactory } from '../../server/ops/cri.js';
15
+ import { ensureContainerStorage } from '../../server/security/container-storage.js';
16
+ import { resolveReplicaCount } from '../../server/runtime/conf.js';
16
17
  // Cyclic by construction (index -> cluster -> MongoBootstrap -> index), same as cluster.js.
17
18
  // Safe because the binding is only dereferenced inside method bodies, never at module scope.
18
19
  import Underpost from '../../index.js';
19
20
  import {
20
21
  MONGODB_DATA_ROOT,
22
+ MONGODB_DEFAULT_AUTH_SOURCE,
23
+ MONGODB_DEFAULT_PORT,
21
24
  MONGODB_DEFAULT_REPLICA_COUNT,
22
25
  MONGODB_DEFAULT_REPLICA_SET,
23
26
  MONGODB_SERVICE_NAME,
@@ -29,6 +32,68 @@ import {
29
32
 
30
33
  const logger = loggerFactory(import.meta);
31
34
 
35
+ const MONGODB_PRIMARY_PROBE_URI =
36
+ `mongodb://127.0.0.1:${MONGODB_DEFAULT_PORT}/${MONGODB_DEFAULT_AUTH_SOURCE}` +
37
+ `?directConnection=true&serverSelectionTimeoutMS=8000&connectTimeoutMS=8000&socketTimeoutMS=15000`;
38
+
39
+ const MONGODB_PRIMARY_PROBE_TIMEOUT_SECONDS = 45;
40
+
41
+ const MONGODB_PRIMARY_EVAL = {
42
+ hello: 'db.hello().primary',
43
+ isMaster: 'rs.isMaster().primary',
44
+ rsStatus: 'rs.status().members.filter(m=>m.stateStr=="PRIMARY").map(m=>m.name)[0]',
45
+ };
46
+
47
+ // Rejected verbatim probe results: a shell that answered but knows no primary.
48
+ const MONGODB_EMPTY_PRIMARY_TOKENS = ['null', 'undefined', '[]', '[ ]', 'false', ''];
49
+
50
+ /**
51
+ * Extracts a pod name from a primary-host probe result.
52
+ *
53
+ * Normalises the two shapes the shells emit — a bare `host:port` string from
54
+ * `hello`, a quoted element from the `rs.status()` projection — and ignores any
55
+ * banner or warning lines printed alongside them.
56
+ *
57
+ * @param {string} output - Raw probe stdout.
58
+ * @returns {string|null} Pod name (`mongodb-0`), or null when no host was reported.
59
+ * @memberof MongoBootstrap
60
+ */
61
+ const parsePrimaryHost = (output) => {
62
+ if (!output) return null;
63
+ const lines = `${output}`
64
+ .split('\n')
65
+ .map((line) => line.trim())
66
+ .filter(Boolean);
67
+
68
+ for (const line of lines.reverse()) {
69
+ const quoted = line.match(/['"]([^'"]+)['"]/);
70
+ const candidate = (quoted ? quoted[1] : line).trim();
71
+ if (MONGODB_EMPTY_PRIMARY_TOKENS.includes(candidate.toLowerCase())) continue;
72
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*(:\d+)?$/.test(candidate)) continue;
73
+ return candidate.split(':')[0].split('.')[0];
74
+ }
75
+ return null;
76
+ };
77
+
78
+ /**
79
+ * Extracts the two replica set identities from a heartbeat rejection.
80
+ *
81
+ * mongod reports `Our replica set ID did not match that of our request target` when two
82
+ * members carry different set IDs — the signature of one member having been re-initiated on
83
+ * an empty volume while the others kept the original set.
84
+ *
85
+ * @param {string} output - Combined stdout/stderr of a mongosh bootstrap attempt.
86
+ * @returns {{selfSetId: string, target: string, targetSetId: string}|null} Parsed identities, or null.
87
+ * @memberof MongoBootstrap
88
+ */
89
+ const parseDivergentSetIds = (output) => {
90
+ const match = `${output || ''}`.match(
91
+ /replSetId:\s*([0-9a-f]+),\s*requestTarget:\s*([^,]+),\s*requestTargetReplSetId:\s*([0-9a-f]+)/,
92
+ );
93
+ if (!match) return null;
94
+ return { selfSetId: match[1], target: match[2].trim(), targetSetId: match[3] };
95
+ };
96
+
32
97
  // Teardown sweeps ordinals rather than a live replica count: the deployed count is not knowable
33
98
  // during a reset, and a member left behind from a larger previous deployment keeps its volume and
34
99
  // stale replica-set config, which a later deploy then trips over.
@@ -90,7 +155,7 @@ class MongoBootstrap {
90
155
  rootUser,
91
156
  rootPassword,
92
157
  }) {
93
- const mePort = '27017';
158
+ const mePort = MONGODB_DEFAULT_PORT;
94
159
  const defaultShortHosts = Array.from(
95
160
  { length: replicaCount },
96
161
  (_, i) => `${statefulSetName}-${i}.${serviceName}:${mePort}`,
@@ -102,8 +167,13 @@ class MongoBootstrap {
102
167
  };
103
168
 
104
169
  return [
105
- `const mePort = "27017";`,
106
170
  `const desiredConfig = ${JSON.stringify(desiredConfig)};`,
171
+ // Single-member bootstrap and recovery configs name this node by the same host it
172
+ // advertises in the final config, never `localhost`. A client that connects while the
173
+ // set is still one member long copies that member into its topology, and inside any
174
+ // other pod `localhost:27017` is that pod itself — the client then sits on an
175
+ // unreachable member and never recovers.
176
+ `const selfHost = desiredConfig.members[0].host;`,
107
177
  `const rootUser = ${JSON.stringify(rootUser)};`,
108
178
  `const rootPassword = ${JSON.stringify(rootPassword)};`,
109
179
 
@@ -166,7 +236,7 @@ class MongoBootstrap {
166
236
  ` const soloConfig = {`,
167
237
  ` _id: desiredConfig._id,`,
168
238
  ` version: currentConfigVersion() + 1,`,
169
- ` members: [{ _id: 0, host: "localhost:" + mePort }],`,
239
+ ` members: [{ _id: 0, host: selfHost }],`,
170
240
  ` };`,
171
241
  ` rs.reconfig(soloConfig, { force: true });`,
172
242
  ` print("SUCCESS_FORCE_RECONFIGURED");`,
@@ -178,7 +248,12 @@ class MongoBootstrap {
178
248
  ` const curHosts = cur.members.map(m => m.host).sort().join(",");`,
179
249
  ` const nextHosts = desiredConfig.members.map(m => m.host).sort().join(",");`,
180
250
  ` if (curHosts !== nextHosts || cur._id !== desiredConfig._id) {`,
181
- ` rs.reconfig({...desiredConfig, version: (cur.version || 1) + 1}, { force: true });`,
251
+ // Not forced: this runs on a node waitPrimary() just confirmed is writable, so an
252
+ // ordinary reconfig applies and bumps the version by one. A forced reconfig instead
253
+ // jumps the config version and election id by a random amount, and drivers that
254
+ // recorded the higher values reject the legitimate primary as stale for the rest of
255
+ // the process lifetime — pods come back as `ReplicaSetNoPrimary` and stay there.
256
+ ` rs.reconfig({...desiredConfig, version: (cur.version || 1) + 1});`,
182
257
  ` print("SUCCESS_RECONFIGURED");`,
183
258
  ` } else {`,
184
259
  ` print("SUCCESS_ALREADY_MATCHES");`,
@@ -212,7 +287,7 @@ class MongoBootstrap {
212
287
 
213
288
  `if (state === "pristine") {`,
214
289
  ` try {`,
215
- ` rs.initiate({ _id: desiredConfig._id, members: [{ _id: 0, host: "localhost:" + mePort }] });`,
290
+ ` rs.initiate({ _id: desiredConfig._id, members: [{ _id: 0, host: selfHost }] });`,
216
291
  ` } catch(e) {`,
217
292
  ` const msg = String(e);`,
218
293
  ` if (!msg.includes("already initialized") && !msg.includes("AlreadyInitialized")) throw e;`,
@@ -301,14 +376,15 @@ class MongoBootstrap {
301
376
  }
302
377
 
303
378
  /**
304
- * Reads MongoDB root credentials from engine-private.
305
- * @param {string} enginePrivateRoot - Path to the engine-private directory.
379
+ * Reads MongoDB root credentials from their origin seed files.
380
+ * Paths come from the workload secret domain, so this never spells out a credential path.
306
381
  * @returns {{ username: string, password: string }}
307
382
  */
308
- static readMongoCredentials(enginePrivateRoot) {
383
+ static readMongoCredentials() {
384
+ const sources = Underpost.secret.seedSources('mongodb-secret');
309
385
  return {
310
- username: MongoBootstrap.readCredential(`${enginePrivateRoot}/mongodb-username`),
311
- password: MongoBootstrap.readCredential(`${enginePrivateRoot}/mongodb-password`),
386
+ username: MongoBootstrap.readCredential(sources.username),
387
+ password: MongoBootstrap.readCredential(sources.password),
312
388
  };
313
389
  }
314
390
 
@@ -324,32 +400,59 @@ class MongoBootstrap {
324
400
  * in the process table and the command log; `disableLog` keeps it out of the log at least. The
325
401
  * encrypted path has no such exposure — the value only ever crosses an anonymous pipe.
326
402
  * @param {string} namespace - Target namespace.
327
- * @param {string} enginePrivateRoot - Path to engine-private directory.
328
403
  */
329
- static ensureMongoSecrets(namespace, enginePrivateRoot) {
330
- if (!Underpost.secret.sops.applyIfPresent('mongodb-keyfile', namespace)) {
331
- const keyfile = MongoBootstrap.readCredential(`${enginePrivateRoot}/mongodb-keyfile`);
404
+ static ensureMongoSecrets(namespace) {
405
+ const keyfilePath = Underpost.secret.seedSources('mongodb-keyfile')['mongodb-keyfile'];
406
+ if (!Underpost.secret.applyIfPresent('mongodb-keyfile', namespace)) {
407
+ const keyfile = MongoBootstrap.readCredential(keyfilePath);
332
408
  shellExec(
333
409
  `sudo kubectl create secret generic mongodb-keyfile` +
334
410
  ` --from-literal=mongodb-keyfile="${keyfile.replace(/'/g, "'\\''")}"` +
335
411
  ` --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
336
412
  { disableLog: true },
337
413
  );
338
- logger.info(`Seeded mongodb-keyfile from ${enginePrivateRoot}/mongodb-keyfile`);
414
+ logger.info(`Seeded mongodb-keyfile from ${keyfilePath}`);
339
415
  }
340
416
 
341
- if (!Underpost.secret.sops.applyIfPresent('mongodb-secret', namespace)) {
342
- const { username, password } = MongoBootstrap.readMongoCredentials(enginePrivateRoot);
417
+ if (!Underpost.secret.applyIfPresent('mongodb-secret', namespace)) {
418
+ const { username, password } = MongoBootstrap.readMongoCredentials();
343
419
  shellExec(
344
420
  `sudo kubectl create secret generic mongodb-secret` +
345
421
  ` --from-literal=username="${username}" --from-literal=password="${password}"` +
346
422
  ` --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
347
423
  { disableLog: true },
348
424
  );
349
- logger.info(`Seeded mongodb-secret from ${enginePrivateRoot}/mongodb-{username,password}`);
425
+ logger.info('Seeded mongodb-secret from origin seed files');
350
426
  }
351
427
  }
352
428
 
429
+ /**
430
+ * Creates the replica data directories and gives the tree the shared container label.
431
+ *
432
+ * The PVs are `hostPath` with `type: DirectoryOrCreate`, so kubelet will happily create a
433
+ * missing member directory — with whatever context its parent carries. `/data` has no mapping
434
+ * in the base RHEL policy, so that context is `default_t`, which `container_t` cannot write:
435
+ * mongod's checkpointer and `ftdc` writer then fail on every `WiredTiger.turtle.set` rename and
436
+ * `metrics.interim.temp` create. Registering the mapping and restoring the tree here makes the
437
+ * label a property of the directory rather than of whoever happened to create it, and it
438
+ * survives a policy update or a full relabel.
439
+ *
440
+ * Only the directories are created; ownership and mode are left to the StatefulSet's
441
+ * `data-dir-permissions` init container, which is the single owner of that concern.
442
+ * @param {number} replicaCount - Number of member directories to prepare.
443
+ * @param {{execute?: Function}} [options] - Command executor, injectable for tests.
444
+ * @returns {string[]} Prepared paths.
445
+ * @memberof MongoBootstrap
446
+ */
447
+ static prepareReplicaDataRoot(replicaCount, { execute = shellExec } = {}) {
448
+ const paths = [
449
+ MONGODB_DATA_ROOT,
450
+ ...Array.from({ length: Math.max(0, Number(replicaCount) || 0) }, (_, i) => `${MONGODB_DATA_ROOT}/v${i}`),
451
+ ];
452
+ ensureContainerStorage(paths, { execute });
453
+ return paths;
454
+ }
455
+
353
456
  /**
354
457
  * Renders one hostPath PersistentVolume per replica, each pre-bound to that member's claim.
355
458
  *
@@ -484,25 +587,6 @@ class MongoBootstrap {
484
587
  );
485
588
  }
486
589
 
487
- /**
488
- * Waits for all MongoDB statefulset pods to reach Running state.
489
- * @param {string} namespace - Target namespace.
490
- * @param {number} replicaCount - Expected number of pods.
491
- * @returns {Promise<number>} Number of pods that failed to become ready (0 = all good).
492
- */
493
- static async waitForPods(namespace, replicaCount) {
494
- let failedCount = 0;
495
- for (let i = 0; i < replicaCount; i++) {
496
- const podName = `${MONGODB_STATEFULSET_NAME}-${i}`;
497
- const result = shellExec(`kubectl wait --for=condition=Ready pod/${podName} -n ${namespace} --timeout=60s`);
498
- if (result.code !== 0) {
499
- logger.error(`Pod ${podName} did not become ready`);
500
- failedCount++;
501
- }
502
- }
503
- return failedCount;
504
- }
505
-
506
590
  /**
507
591
  * Full MongoDB replica set initialization.
508
592
  *
@@ -523,7 +607,6 @@ class MongoBootstrap {
523
607
  underpostRoot,
524
608
  } = options;
525
609
 
526
- const enginePrivateRoot = `${process.cwd()}/engine-private`;
527
610
  // No upward clamp: an explicit `--replicas 2` must deploy two members, not be silently
528
611
  // raised to the default.
529
612
  const effectiveReplicaCount = resolveReplicaCount(replicaCount, MONGODB_DEFAULT_REPLICA_COUNT);
@@ -533,8 +616,7 @@ class MongoBootstrap {
533
616
  `majority when one member is down, so the set becomes read-only on a single failure. ` +
534
617
  `Odd counts (3, 5) are recommended.`,
535
618
  );
536
- const mongoRootUsername = MongoBootstrap.readCredential(`${enginePrivateRoot}/mongodb-username`);
537
- const mongoRootPassword = MongoBootstrap.readCredential(`${enginePrivateRoot}/mongodb-password`);
619
+ const { username: mongoRootUsername, password: mongoRootPassword } = MongoBootstrap.readMongoCredentials();
538
620
  const mongoReplicaHosts = resolveMongoReplicaHosts({
539
621
  hostList,
540
622
  replicaCount: effectiveReplicaCount,
@@ -578,7 +660,7 @@ class MongoBootstrap {
578
660
  }
579
661
 
580
662
  // Secrets
581
- MongoBootstrap.ensureMongoSecrets(namespace, enginePrivateRoot);
663
+ MongoBootstrap.ensureMongoSecrets(namespace);
582
664
 
583
665
  // Tear down existing statefulset
584
666
  shellExec(`kubectl delete statefulset ${MONGODB_STATEFULSET_NAME} -n ${namespace} --ignore-not-found`);
@@ -614,8 +696,11 @@ class MongoBootstrap {
614
696
  shellExec(`sudo mkdir -p ${MONGODB_DATA_ROOT}`);
615
697
  for (let i = 0; i < effectiveReplicaCount; i++) {
616
698
  shellExec(`sudo rm -rf ${MONGODB_DATA_ROOT}/v${i}`);
617
- shellExec(`sudo mkdir -p ${MONGODB_DATA_ROOT}/v${i}`);
618
699
  }
700
+ // A directory recreated after a wipe inherits its parent's context, and the WiredTiger
701
+ // files mongod then creates inherit that in turn — so recreate and relabel here, before the
702
+ // Kind remount below binds these inodes into the node containers.
703
+ MongoBootstrap.prepareReplicaDataRoot(effectiveReplicaCount);
619
704
  // Fix any stale bind mounts caused by prior deletion of /data/mongodb on the host
620
705
  if (isKind) MongoBootstrap.remountKindMongoVolume(kindNodes, effectiveReplicaCount);
621
706
  }
@@ -637,6 +722,10 @@ class MongoBootstrap {
637
722
  shellExec(`kubectl delete storageclass ${MONGODB_STORAGE_CLASS_NAME} --ignore-not-found`);
638
723
  }
639
724
  shellExec(`kubectl apply -f ${underpostRoot}/manifests/mongodb/storage-class.yaml -n ${namespace}`);
725
+ // Unconditional, not only on the reset path: an existing cluster's member directories were
726
+ // created before the mapping existed (or by kubelet's `DirectoryOrCreate`), so they carry the
727
+ // policy default until this runs. Idempotent, and it never touches the data itself.
728
+ MongoBootstrap.prepareReplicaDataRoot(effectiveReplicaCount);
640
729
  // One PV per member, generated from the effective replica count. A static manifest can only
641
730
  // ever describe a fixed number, so any `--replicas` above it leaves the extra members with no
642
731
  // volume to bind and the StatefulSet stalls forever on Pending.
@@ -647,13 +736,16 @@ class MongoBootstrap {
647
736
  );
648
737
 
649
738
  // Wait for all pods
650
- const failedCount = await MongoBootstrap.waitForPods(namespace, effectiveReplicaCount);
651
- if (failedCount > 0) {
739
+ const failedPods = await Underpost.kubectl.waitForPodsReady({
740
+ namespace,
741
+ podNames: Array.from({ length: effectiveReplicaCount }, (_, i) => `${MONGODB_STATEFULSET_NAME}-${i}`),
742
+ });
743
+ if (failedPods.length > 0) {
652
744
  // Surface the volume topology before failing: a stalled rollout is far more often a binding
653
745
  // problem than a MongoDB one, and the mapping names it immediately.
654
746
  MongoBootstrap.reportReplicaVolumeBindings(namespace, effectiveReplicaCount);
655
747
  throw new Error(
656
- `MongoDB replica pods did not reach Running state in time. ` +
748
+ `MongoDB replica pods did not reach Running state in time: ${failedPods.join(', ')}. ` +
657
749
  `Ensure podManagementPolicy is set to OrderedReady in statefulset.yaml.`,
658
750
  );
659
751
  }
@@ -663,7 +755,7 @@ class MongoBootstrap {
663
755
  // Build the bootstrap script
664
756
  const defaultHosts = Array.from(
665
757
  { length: effectiveReplicaCount },
666
- (_, i) => `${MONGODB_STATEFULSET_NAME}-${i}.${MONGODB_SERVICE_NAME}:27017`,
758
+ (_, i) => `${MONGODB_STATEFULSET_NAME}-${i}.${MONGODB_SERVICE_NAME}:${MONGODB_DEFAULT_PORT}`,
667
759
  );
668
760
  const desiredHosts = useExplicitHosts ? mongoReplicaHosts : defaultHosts;
669
761
 
@@ -697,32 +789,46 @@ class MongoBootstrap {
697
789
  );
698
790
  };
699
791
 
792
+ // Two members holding different replica set IDs cannot be merged — every heartbeat
793
+ // between them is rejected — so retrying only repeats the same failure. Diagnose it once
794
+ // and stop: choosing which identity survives means discarding the other member's data,
795
+ // which is never safe to decide automatically.
796
+ const assertSetIdentityMatch = (result) => {
797
+ const divergent = parseDivergentSetIds(`${result.stdout || ''}${result.stderr || ''}`);
798
+ if (!divergent) return;
799
+ throw new Error(
800
+ `MongoDB members belong to different replica sets: ${MONGODB_STATEFULSET_NAME}-0 holds replSetId ` +
801
+ `${divergent.selfSetId}, ${divergent.target} holds ${divergent.targetSetId}. A set cannot merge two ` +
802
+ `identities. Find which volume under ${MONGODB_DATA_ROOT} holds the data to keep, back it up, then ` +
803
+ `wipe the other members' volumes so they resync from the survivor.`,
804
+ );
805
+ };
806
+
700
807
  let success = false;
701
808
  const maxAttempts = 5;
702
809
 
703
810
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
704
811
  const noAuthResult = execMongoCmd(false);
705
- if (noAuthResult.code === 0) {
706
- if (mongoRootUsername && mongoRootPassword) {
707
- const authResult = execMongoCmd(true);
708
- if (authResult.code === 0) {
709
- success = true;
710
- break;
711
- }
712
- logger.warn('No-auth bootstrap succeeded but auth verify failed, retrying...', { attempt });
713
- } else {
714
- success = true;
715
- break;
716
- }
717
- } else {
718
- const authResult = execMongoCmd(true);
719
- if (authResult.code === 0) {
720
- success = true;
721
- break;
722
- }
723
- logger.warn('Both bootstrap paths failed, retrying...', { attempt });
812
+ assertSetIdentityMatch(noAuthResult);
813
+
814
+ if (noAuthResult.code === 0 && !(mongoRootUsername && mongoRootPassword)) {
815
+ success = true;
816
+ break;
724
817
  }
725
818
 
819
+ const authResult = execMongoCmd(true);
820
+ assertSetIdentityMatch(authResult);
821
+ if (authResult.code === 0) {
822
+ success = true;
823
+ break;
824
+ }
825
+ logger.warn(
826
+ noAuthResult.code === 0
827
+ ? 'No-auth bootstrap succeeded but auth verify failed, retrying...'
828
+ : 'Both bootstrap paths failed, retrying...',
829
+ { attempt },
830
+ );
831
+
726
832
  if (attempt < maxAttempts) {
727
833
  await new Promise((r) => setTimeout(r, 3000));
728
834
  }
@@ -780,7 +886,7 @@ class MongoBootstrap {
780
886
  // Also catch any remaining PVs with the app=mongodb label
781
887
  shellExec(`kubectl delete pv -l app=mongodb --ignore-not-found`);
782
888
  // Wait for PVs to be fully deleted to avoid "object modified" conflict on re-apply
783
- shellExec(`kubectl wait --for=delete pv mongodb-pv-0 mongodb-pv-1 mongodb-pv-2 mongodb-pv --timeout=60s`, {
889
+ shellExec(`kubectl wait --for=delete pv mongodb-pv-0 mongodb-pv-1 mongodb-pv-2 mongodb-pv --timeout=180s`, {
784
890
  silentOnError: true,
785
891
  });
786
892
 
@@ -794,8 +900,8 @@ class MongoBootstrap {
794
900
  shellExec(`sudo mkdir -p ${MONGODB_DATA_ROOT}`);
795
901
  for (let i = 0; i < MONGODB_ORDINAL_SWEEP; i++) {
796
902
  shellExec(`sudo rm -rf ${MONGODB_DATA_ROOT}/v${i}`);
797
- shellExec(`sudo mkdir -p ${MONGODB_DATA_ROOT}/v${i}`);
798
903
  }
904
+ MongoBootstrap.prepareReplicaDataRoot(MONGODB_ORDINAL_SWEEP);
799
905
  // For Kind: repair any stale bind mounts via nsenter (overmounts with current host inode)
800
906
  if (isKind) {
801
907
  const nodesRaw = shellExec('kind get nodes', { stdout: true, silent: true, silentOnError: true });
@@ -819,13 +925,22 @@ class MongoBootstrap {
819
925
 
820
926
  /**
821
927
  * Gets the primary MongoDB pod name from replica set status.
928
+ *
929
+ * Probing is bounded on every axis, because an unbounded probe is what makes the
930
+ * backup pipeline stall: the driver connects `directConnection` so it never tries
931
+ * to reach sibling members by DNS, each shell call carries explicit server-selection
932
+ * timeouts, and the whole `kubectl exec` is wrapped in a wall-clock budget. The
933
+ * cheapest probe (`hello`, which the server answers before authentication) runs
934
+ * first, so a healthy replica set resolves without credentials at all.
935
+ *
822
936
  * @param {object} [options] - Query options.
823
937
  * @param {string} [options.namespace='default'] - Kubernetes namespace.
824
- * @param {string} [options.podName='mongodb-0'] - Any MongoDB pod to query.
938
+ * @param {string} [options.podName='mongodb-0'] - Preferred MongoDB pod to query first.
825
939
  * @param {string} [options.username] - MongoDB admin username.
826
940
  * @param {string} [options.password] - MongoDB admin password.
827
941
  * @param {string} [options.authDatabase='admin'] - Auth database.
828
942
  * @param {boolean} [options.disableAuth=false] - Whether to disable auth in the query (for testing).
943
+ * @param {number} [options.probeTimeoutSeconds=45] - Wall-clock budget per shell probe.
829
944
  * @returns {string|null} Primary pod name, or null if not found.
830
945
  */
831
946
  static getPrimaryPodName(options = {}) {
@@ -836,6 +951,7 @@ class MongoBootstrap {
836
951
  password,
837
952
  authDatabase = 'admin',
838
953
  disableAuth = false,
954
+ probeTimeoutSeconds = MONGODB_PRIMARY_PROBE_TIMEOUT_SECONDS,
839
955
  } = options;
840
956
 
841
957
  const readTrimmedFile = (filePath) => {
@@ -851,48 +967,104 @@ class MongoBootstrap {
851
967
  username ||
852
968
  process.env.MONGODB_USERNAME ||
853
969
  process.env.DB_USER ||
854
- readTrimmedFile('./engine-private/mongodb-username');
970
+ readTrimmedFile(Underpost.secret.seedSources('mongodb-secret').username);
855
971
  const mongoPass =
856
972
  password ||
857
973
  process.env.MONGODB_PASSWORD ||
858
974
  process.env.DB_PASSWORD ||
859
- readTrimmedFile('./engine-private/mongodb-password');
975
+ readTrimmedFile(Underpost.secret.seedSources('mongodb-secret').password);
976
+
977
+ const candidates = MongoBootstrap.resolvePrimaryProbeCandidates({ namespace, podName });
978
+ const probes = MongoBootstrap.primaryProbeFactory({
979
+ disableAuth,
980
+ username: mongoUser,
981
+ password: mongoPass,
982
+ authDatabase,
983
+ });
860
984
 
861
- // Ensure the pod is ready before querying
862
- shellExec(`kubectl wait --for=condition=Ready pod/${podName} -n ${namespace} --timeout=60s`);
985
+ for (const candidate of candidates) {
986
+ if (!Underpost.kubectl.ensureExecReady({ podName: candidate, namespace })) continue;
863
987
 
864
- const evalExpr = 'rs.status().members.filter(m=>m.stateStr=="PRIMARY").map(m=>m.name)';
988
+ for (const probe of probes) {
989
+ const output = shellExec(
990
+ `timeout --kill-after=10s ${probeTimeoutSeconds}s ` +
991
+ `sudo kubectl exec -n ${namespace} -i ${candidate} -- ${probe.command}`,
992
+ { stdout: true, silent: true, silentOnError: true, disableLog: true },
993
+ );
994
+ const primary = parsePrimaryHost(output);
995
+ if (primary) {
996
+ logger.info('Found MongoDB primary pod', { primary, probedFrom: candidate, probe: probe.id });
997
+ return primary;
998
+ }
999
+ }
865
1000
 
866
- const cli = disableAuth ? 'mongo' : 'mongosh';
1001
+ logger.warn('MongoDB pod answered no primary', { podName: candidate, namespace });
1002
+ }
867
1003
 
868
- let output = shellExec(`sudo kubectl exec -n ${namespace} -i ${podName} -- ${cli} --quiet --eval '${evalExpr}'`, {
869
- stdout: true,
870
- silent: true,
871
- silentOnError: true,
872
- });
1004
+ logger.warn('No MongoDB primary pod found.', { namespace, candidates });
1005
+ return null;
1006
+ }
873
1007
 
874
- if (!disableAuth && (!output || output.trim() === '') && mongoUser && mongoPass) {
875
- output = shellExec(
876
- `sudo kubectl exec -n ${namespace} -i ${podName} -- mongosh --quiet ` +
877
- `--authenticationDatabase ${JSON.stringify(authDatabase)} ` +
878
- `-u ${JSON.stringify(mongoUser)} -p ${JSON.stringify(mongoPass)} --eval '${evalExpr}'`,
879
- { stdout: true, silent: true, silentOnError: true },
880
- );
1008
+ /**
1009
+ * Resolves which pods to interrogate for the replica-set primary, preferred pod first.
1010
+ *
1011
+ * Any reachable member knows who the primary is, so a member that is mid-restart
1012
+ * must not end the search — the caller would otherwise fall back to a pod that is
1013
+ * merely first in the list rather than the one accepting writes.
1014
+ *
1015
+ * @param {object} params
1016
+ * @param {string} params.namespace - Kubernetes namespace.
1017
+ * @param {string} params.podName - Preferred pod to probe first.
1018
+ * @returns {string[]} Ordered, de-duplicated pod names.
1019
+ */
1020
+ static resolvePrimaryProbeCandidates({ namespace, podName }) {
1021
+ const candidates = podName ? [podName] : [];
1022
+ try {
1023
+ for (const pod of Underpost.kubectl.get(MONGODB_STATEFULSET_NAME, 'pods', namespace)) {
1024
+ if (!pod.NAME || pod.STATUS !== 'Running') continue;
1025
+ if (!candidates.includes(pod.NAME)) candidates.push(pod.NAME);
1026
+ }
1027
+ } catch (error) {
1028
+ logger.warn('Could not list MongoDB pods, probing the requested pod only', {
1029
+ namespace,
1030
+ error: error.message,
1031
+ });
881
1032
  }
1033
+ return candidates;
1034
+ }
882
1035
 
883
- if (!output || output.trim() === '') {
884
- logger.warn('No MongoDB primary pod found.');
885
- return null;
886
- }
1036
+ /**
1037
+ * Builds the ordered probe list used to resolve the replica-set primary.
1038
+ * @param {object} params
1039
+ * @param {boolean} params.disableAuth - Legacy `mongo` shell without credentials.
1040
+ * @param {string} params.username - MongoDB username, when available.
1041
+ * @param {string} params.password - MongoDB password, when available.
1042
+ * @param {string} params.authDatabase - Auth database for credentialed probes.
1043
+ * @returns {Array<{id: string, command: string}>} Probes in cheapest-first order.
1044
+ */
1045
+ static primaryProbeFactory({ disableAuth, username, password, authDatabase }) {
1046
+ if (disableAuth)
1047
+ return [
1048
+ { id: 'legacy-isMaster', command: `mongo --quiet --eval '${MONGODB_PRIMARY_EVAL.isMaster}'` },
1049
+ { id: 'legacy-rs-status', command: `mongo --quiet --eval '${MONGODB_PRIMARY_EVAL.rsStatus}'` },
1050
+ ];
1051
+
1052
+ const shell = `mongosh ${JSON.stringify(MONGODB_PRIMARY_PROBE_URI)} --quiet`;
1053
+ const authFlags =
1054
+ username && password
1055
+ ? ` --authenticationDatabase ${JSON.stringify(authDatabase)} -u ${JSON.stringify(username)} -p ${JSON.stringify(password)}`
1056
+ : '';
887
1057
 
888
- const match = output.match(/['"]([^'"]+)['"]/);
889
- if (match && match[1]) {
890
- const primary = match[1].split(':')[0].split('.')[0];
891
- logger.info('Found MongoDB primary pod', { primary });
892
- return primary;
1058
+ // `hello` is answered before authentication, so it resolves the primary even when
1059
+ // the credentials at hand are an app user without cluster-monitor rights.
1060
+ const probes = [{ id: 'hello', command: `${shell} --eval '${MONGODB_PRIMARY_EVAL.hello}'` }];
1061
+ if (authFlags) {
1062
+ probes.push({ id: 'hello-auth', command: `${shell}${authFlags} --eval '${MONGODB_PRIMARY_EVAL.hello}'` });
1063
+ probes.push({ id: 'rs-status-auth', command: `${shell}${authFlags} --eval '${MONGODB_PRIMARY_EVAL.rsStatus}'` });
893
1064
  }
894
- return null;
1065
+ probes.push({ id: 'rs-status', command: `${shell} --eval '${MONGODB_PRIMARY_EVAL.rsStatus}'` });
1066
+ return probes;
895
1067
  }
896
1068
  }
897
1069
 
898
- export { MongoBootstrap };
1070
+ export { MongoBootstrap, parsePrimaryHost, parseDivergentSetIds, MONGODB_PRIMARY_PROBE_URI };