@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
@@ -0,0 +1,382 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @module prepare-host.test
5
+ * @description Covers the step order `prepare_host` in `deploy/lib/host.sh` emits. Drives the real
6
+ * shell helper with `deploy_step` stubbed out, so nothing is executed — no host, no root.
7
+ *
8
+ * Uses 'chai' for assertions.
9
+ */
10
+
11
+ import { expect } from 'chai';
12
+ import { execFileSync } from 'node:child_process';
13
+ import fs from 'fs-extra';
14
+ import os from 'node:os';
15
+ import path from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
19
+ const hostLib = path.join(repoRoot, 'deploy/lib/host.sh');
20
+ // Defined before and after the source: the library pulls in the real logging helpers, which
21
+ // would otherwise run the steps instead of printing them.
22
+ const STUB = `deploy_step() { printf '%s :: %s\\n' "$1" "${'$'}{*:2}"; };`;
23
+
24
+ const steps = () =>
25
+ execFileSync(
26
+ 'bash',
27
+ [
28
+ '-c',
29
+ `deploy_step() { printf '%s :: %s\\n' "$1" "${'$'}{*:2}"; }; source "${hostLib}"; ` +
30
+ `prepare_host /home/dd/engine owner/engine owner/engine-private`,
31
+ ],
32
+ { encoding: 'utf8' },
33
+ )
34
+ .trim()
35
+ .split('\n');
36
+
37
+ describe('a node installs the deploy own package manifest', () => {
38
+ const run = (script) =>
39
+ execFileSync('bash', ['-c', `${STUB} source "${hostLib}"; ${STUB} ${script}`], {
40
+ encoding: 'utf8',
41
+ });
42
+
43
+ it('resolves the deploy id from the source repository, without running the CLI', () => {
44
+ // The manifest this resolves is what makes the CLI runnable, so it cannot be resolved by
45
+ // asking the CLI. Test and production sources of one deploy answer the same id.
46
+ const resolved = run(
47
+ 'for r in owner/engine-test-cyberia owner/engine-cyberia owner/engine-cyberia-private owner/engine; ' +
48
+ 'do printf "[%s]" "$(deploy_id_from_repo "$r")"; done',
49
+ );
50
+ expect(resolved.trim()).to.equal('[dd-cyberia][dd-cyberia][dd-cyberia][]');
51
+ });
52
+
53
+ it('installs from engine-private/conf/<deploy-id>/package.json, on both sides of the pull', () => {
54
+ // Regression: a product checkout declares only what it publishes, so installing against its
55
+ // own manifest pruned packages the CLI imports — and the CLI is what pulls the checkout.
56
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'underpost-prepare-host-'));
57
+ try {
58
+ fs.outputFileSync(path.join(root, 'engine-private/conf/dd-x/package.json'), '{}');
59
+ const emitted = run(`prepare_host ${root} owner/engine-x owner/engine-private`).trim().split('\n');
60
+ const titles = emitted.map((line) => line.split(' :: ')[0]);
61
+
62
+ expect(titles).to.deep.equal([
63
+ 'Install dependencies (dd-x)',
64
+ 'Link underpost CLI',
65
+ 'Pull repository',
66
+ 'Install dependencies (dd-x)',
67
+ 'Link underpost CLI',
68
+ 'Load host config',
69
+ ]);
70
+ for (const index of [0, 3])
71
+ expect(emitted[index]).to.include(
72
+ 'cp -a ./engine-private/conf/dd-x/package.json ./package.json && npm install',
73
+ );
74
+ } finally {
75
+ fs.removeSync(root);
76
+ }
77
+ });
78
+
79
+ it('installs the checkout own manifest when the deploy ships none', () => {
80
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'underpost-prepare-host-'));
81
+ try {
82
+ const emitted = run(`install_deploy_dependencies ${root} dd-absent`).trim().split('\n');
83
+ expect(emitted[0]).to.equal(`Install dependencies :: sudo -n -- /bin/bash -lc cd ${root} && npm install`);
84
+ // The link is a symlink onto the checkout's entrypoint, and a restored checkout can leave
85
+ // that file without its executable mode, so the link step restores it before linking.
86
+ expect(emitted[1]).to.include(
87
+ `Link underpost CLI :: sudo -n -- /bin/bash -lc cd ${root} && chmod +x ./bin/index.js && npm link --force`,
88
+ );
89
+ expect(emitted[1]).to.include('test -x ./bin/index.js');
90
+ } finally {
91
+ fs.removeSync(root);
92
+ }
93
+ });
94
+ });
95
+
96
+ describe('a node prepares itself from the source its host configuration names', () => {
97
+ // `npm root -g` is stubbed rather than the file read: the store's location is the one thing
98
+ // the helper resolves, and a test that writes into the real global tree is not a unit test.
99
+ const sources = (store) =>
100
+ execFileSync(
101
+ 'bash',
102
+ [
103
+ '-c',
104
+ `npm() { printf '%s\\n' "${store}"; }; source "${hostLib}"; ` +
105
+ 'printf "[%s][%s]" "$ENGINE_SRC_REPO" "$ENGINE_SRC_PRIVATE_REPO"',
106
+ ],
107
+ { encoding: 'utf8', env: { ...process.env, ENGINE_SRC_REPO: '', ENGINE_SRC_PRIVATE_REPO: '' } },
108
+ );
109
+
110
+ it('reads the pair `wireguard --sync` recorded, over the built-in fallback', () => {
111
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'underpost-host-config-'));
112
+ try {
113
+ fs.outputFileSync(
114
+ path.join(root, 'underpost/.env'),
115
+ 'GITHUB_TOKEN=secret\nENGINE_SRC_REPO="owner/engine-test-lampp"\nENGINE_SRC_PRIVATE_REPO=owner/engine-lampp-private\n',
116
+ );
117
+ expect(sources(root)).to.equal('[owner/engine-test-lampp][owner/engine-lampp-private]');
118
+ } finally {
119
+ fs.removeSync(root);
120
+ }
121
+ });
122
+
123
+ it('names no repository of its own, leaving the pairing to the pull that resolves it', () => {
124
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'underpost-host-config-'));
125
+ try {
126
+ expect(sources(root)).to.equal('[][]');
127
+ } finally {
128
+ fs.removeSync(root);
129
+ }
130
+ });
131
+
132
+ it('lets the environment override the store, which is how a deploy script retargets a node', () => {
133
+ const emitted = execFileSync('bash', ['-c', `source "${hostLib}"; printf '%s' "$(deploy_id_from_repo)"`], {
134
+ encoding: 'utf8',
135
+ env: { ...process.env, ENGINE_SRC_REPO: 'owner/engine-test-core' },
136
+ });
137
+ expect(emitted).to.equal('dd-core');
138
+ });
139
+ });
140
+
141
+ const deployRoot = path.join(repoRoot, 'deploy');
142
+ // The base template restores `deploy/lib` and nothing that deploys, so a checkout carrying no
143
+ // deploy id of its own has no manifest script to hold to this contract. Read as "ships deploy
144
+ // ids", not as "the filter below found something" — that stays an assertion, not a skip.
145
+ const shipsDeployIds = fs.existsSync(deployRoot) && fs.readdirSync(deployRoot).some((e) => e.startsWith('dd-'));
146
+
147
+ describe.skipIf(!shipsDeployIds)('every deploy ships a package script for its own manifest', () => {
148
+ // A deploy id directory is one that carries a deploy script; lib/ and the project-scope
149
+ // directories are not deployments.
150
+ const deployDirectories = fs
151
+ .readdirSync(deployRoot)
152
+ .filter((entry) => fs.existsSync(path.join(deployRoot, entry, 'sync-deploy.sh')) || entry.startsWith('cyberia-'));
153
+
154
+ it('covers every deploy id and instance directory', () => {
155
+ expect(deployDirectories).to.not.be.empty;
156
+ for (const directory of deployDirectories)
157
+ expect(fs.existsSync(path.join(deployRoot, directory, 'package.sh')), directory).to.equal(true);
158
+ });
159
+
160
+ it('names a deploy id and does nothing but install its manifest', () => {
161
+ for (const directory of deployDirectories) {
162
+ const source = fs.readFileSync(path.join(deployRoot, directory, 'package.sh'), 'utf8');
163
+ expect(source, directory).to.match(/^DEPLOY_ID=dd-[a-z0-9-]+$/m);
164
+ expect(source, directory).to.include('install_deploy_dependencies "$ENGINE_ROOT" "$DEPLOY_ID"');
165
+ expect(source, directory).to.not.include('kubectl');
166
+ }
167
+ });
168
+ });
169
+
170
+ describe('node bootstrap installs a runtime a unit can execute', () => {
171
+ // Regression: both scripts said "home-directory runtimes cannot be used by hardened systemd
172
+ // services under SELinux" and then gated on `command -v node`, which an nvm runtime under $HOME
173
+ // satisfies. A node whose operator had nvm Node 24 on PATH was left with no system Node at all,
174
+ // and every supervised service failed 203/EXEC with nothing able to repair it.
175
+ const scripts = ['scripts/kubeadm-node-setup.sh', 'scripts/k3s-node-setup.sh'];
176
+
177
+ it('decides on a system path rather than on whatever is on PATH', () => {
178
+ for (const script of scripts) {
179
+ const source = fs.readFileSync(path.join(repoRoot, script), 'utf8');
180
+ expect(source, script).to.include('system_node_path()');
181
+ expect(source, script).to.include('/usr/bin/node /usr/local/bin/node /bin/node');
182
+ expect(source, script).to.not.match(/^\s*if.*command -v node .*grep -q .\^v24/m);
183
+ }
184
+ });
185
+
186
+ it('resolves only an executable Node 24 at a system path', () => {
187
+ // The candidate paths are faked under a temp root rather than the real /usr/bin, /usr/local/bin,
188
+ // /bin: the host running this suite may itself have a genuine system Node 24 at one of those
189
+ // paths, which would make the probe correctly resolve it and defeat the regression check below.
190
+ const probe = (bin, candidates) =>
191
+ execFileSync(
192
+ 'bash',
193
+ [
194
+ '-c',
195
+ 'set -euo pipefail; system_node_path() { local c; for c in ' +
196
+ candidates +
197
+ '; do if [ -x "$c" ] && "$c" --version 2>/dev/null | grep -q "^v24"; then printf "%s" "$c"; return 0; fi; ' +
198
+ `done; return 1; }; PATH=${bin}:$PATH; if p="$(system_node_path)"; then printf 'system:%s' "$p"; ` +
199
+ "else printf 'none'; fi",
200
+ ],
201
+ { encoding: 'utf8' },
202
+ );
203
+
204
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'underpost-nvm-'));
205
+ try {
206
+ const home = path.join(root, 'home');
207
+ const candidates = ['/usr/bin', '/usr/local/bin', '/bin'].map((d) => path.join(root, 'fake', d, 'node'));
208
+
209
+ // A Node 24 reachable only through PATH, the way nvm publishes one, resolves to nothing.
210
+ fs.outputFileSync(path.join(home, 'node'), '#!/bin/sh\necho v24.15.0\n');
211
+ fs.chmodSync(path.join(home, 'node'), 0o755);
212
+ expect(probe(home, candidates.join(' '))).to.equal('none');
213
+ } finally {
214
+ fs.removeSync(root);
215
+ }
216
+ });
217
+ });
218
+
219
+ describe('the pod bootstrap can write what it installs', () => {
220
+ const bootstrap = (args) =>
221
+ execFileSync('bash', ['-c', `source "${hostLib}"; pod_bootstrap_cmd ${args}`], { encoding: 'utf8' })
222
+ .split(',')
223
+ .map((step) => step.trim());
224
+
225
+ it('takes the global npm prefix before installing or linking into it', () => {
226
+ // Regression: the manifest's `install` hook installs global tooling and `npm link` publishes
227
+ // the CLI there, and the image leaves that tree owned by root — both failed with EACCES.
228
+ const steps = bootstrap('dd-cyberia production owner/engine-test-cyberia');
229
+ const grant = steps.findIndex((step) => step.startsWith('sudo chown -R'));
230
+
231
+ expect(grant, 'ownership of the npm prefix').to.be.greaterThan(-1);
232
+ expect(steps[grant]).to.include('$(npm prefix -g)/lib/node_modules');
233
+ expect(steps[grant]).to.include('$(npm prefix -g)/bin');
234
+ // Resolved in the container, by the user the container runs as.
235
+ expect(steps[grant]).to.include('$(id -u):$(id -g)');
236
+ expect(grant).to.be.lessThan(steps.indexOf('npm install'));
237
+ expect(steps.indexOf('npm install')).to.be.lessThan(steps.indexOf('npm link --force'));
238
+ const verifyLink = steps.findIndex((step) => step.includes('command -v underpost'));
239
+ const setStatus = steps.findIndex((step) => step.startsWith('underpost state set'));
240
+ expect(verifyLink, 'linked underpost path verification').to.be.greaterThan(steps.indexOf('npm link --force'));
241
+ expect(verifyLink).to.be.lessThan(setStatus);
242
+ expect(steps[verifyLink]).to.include('readlink -f ./bin/index.js');
243
+ });
244
+ });
245
+
246
+ describe('has_changes reads a checkout the caller names', () => {
247
+ const query = (call) =>
248
+ execFileSync('bash', ['-c', `source "${hostLib}"; sudo() { printf '%s\\n' "$*"; }; ${call}`], {
249
+ encoding: 'utf8',
250
+ }).trim();
251
+
252
+ it('takes the engine root as an argument, and falls back to the caller own root', () => {
253
+ expect(query('has_changes src/x /tmp/other-root')).to.include('cd /tmp/other-root');
254
+ expect(query('ENGINE_ROOT=/inherited-root; has_changes src/y')).to.include('cd /inherited-root');
255
+ expect(query('has_changes src/z')).to.include('cd /home/dd/engine');
256
+ });
257
+
258
+ it('asks the engine for the status of the path it was given', () => {
259
+ expect(query('has_changes src/client/public/cyberia /engine')).to.include(
260
+ 'node bin cmt src/client/public/cyberia --has-changes',
261
+ );
262
+ });
263
+ });
264
+
265
+ describe('prepare_host brings a node up in an order it can recover from', () => {
266
+ const emitted = steps();
267
+ const titles = emitted.map((line) => line.split(' :: ')[0]);
268
+ const step = (title) => emitted[titles.indexOf(title)] || '';
269
+
270
+ it('installs dependencies before running anything from the checkout', () => {
271
+ // Regression: the pull runs through this checkout's own CLI, so a tree whose node_modules
272
+ // no longer match its package.json failed to import — and the step that would have replaced
273
+ // the source never ran. Only the first install is load-bearing; a node reinstalled after the
274
+ // pull as well is fine.
275
+ expect(titles[0]).to.equal('Install dependencies');
276
+ expect(emitted[0]).to.include('npm install');
277
+ expect(titles.indexOf('Pull repository')).to.be.greaterThan(0);
278
+ });
279
+
280
+ it('relinks the checkout CLI after every dependency install', () => {
281
+ const installs = titles.reduce((indexes, title, index) => {
282
+ if (title.startsWith('Install dependencies')) indexes.push(index);
283
+ return indexes;
284
+ }, []);
285
+
286
+ expect(installs).to.have.lengthOf(2);
287
+ for (const index of installs) {
288
+ expect(titles[index + 1]).to.equal('Link underpost CLI');
289
+ expect(emitted[index + 1]).to.include('npm link --force');
290
+ expect(emitted[index + 1]).to.include('command -v underpost');
291
+ expect(emitted[index + 1]).to.include('readlink -f ./bin/index.js');
292
+ }
293
+ });
294
+
295
+ it('replaces the checkout through the engine CLI, with both repositories named', () => {
296
+ expect(step('Pull repository')).to.include('node bin run pull owner/engine');
297
+ expect(step('Pull repository')).to.include('--repo-engine-private owner/engine-private');
298
+ });
299
+
300
+ it('loads the host config last, through the one entry point for that store', () => {
301
+ expect(titles.at(-1)).to.equal('Load host config');
302
+ expect(emitted.at(-1)).to.include('node bin host load');
303
+ });
304
+ });
305
+
306
+ const cyberiaDeployScript = path.join(deployRoot, 'dd-cyberia/sync-deploy.sh');
307
+
308
+ describe.skipIf(!fs.existsSync(cyberiaDeployScript))('a pod lands its private configuration at engine-private', () => {
309
+ const source = fs.existsSync(cyberiaDeployScript) ? fs.readFileSync(cyberiaDeployScript, 'utf8') : '';
310
+
311
+ it('names the pod conf repository once and derives the directory the clone lands in', () => {
312
+ expect(source).to.match(/^POD_SRC_PRIVATE_REPO="\$\{POD_SRC_PRIVATE_REPO:-[^"]+\}"$/m);
313
+ expect(source).to.include('underpost clone ${POD_SRC_PRIVATE_REPO}');
314
+ expect(source).to.include('POD_SRC_PRIVATE_DIR="${POD_SRC_PRIVATE_REPO##*/}"');
315
+ expect(source).to.include('sudo mv ./${POD_SRC_PRIVATE_DIR} ./engine-private');
316
+ });
317
+
318
+ it('clears the destination first, so the checkout replaces engine-private instead of nesting in it', () => {
319
+ const cleared = source.indexOf('sudo rm -rf ./engine-private');
320
+ const moved = source.indexOf('sudo mv ./${POD_SRC_PRIVATE_DIR} ./engine-private');
321
+ expect(cleared).to.be.greaterThan(-1);
322
+ expect(cleared).to.be.lessThan(moved);
323
+ });
324
+ });
325
+
326
+ // Every script under deploy/ is the same shape on purpose: one entry point, one constants block,
327
+ // and no value repeated across files. A second shape is how the drift starts, so it is asserted
328
+ // here rather than left to review.
329
+ describe.skipIf(!shipsDeployIds)('every deploy script is written to one shape', () => {
330
+ const libDirectory = path.join(deployRoot, 'lib');
331
+ const scripts = fs
332
+ .readdirSync(deployRoot)
333
+ .filter((entry) => fs.statSync(path.join(deployRoot, entry)).isDirectory() && entry !== 'lib')
334
+ .flatMap((directory) =>
335
+ fs
336
+ .readdirSync(path.join(deployRoot, directory))
337
+ .filter((file) => file.endsWith('.sh'))
338
+ .map((file) => ({
339
+ id: `${directory}/${file}`,
340
+ source: fs.readFileSync(path.join(deployRoot, directory, file), 'utf8'),
341
+ })),
342
+ );
343
+ const libraries = fs
344
+ .readdirSync(libDirectory)
345
+ .filter((file) => file.endsWith('.sh'))
346
+ .map((file) => ({ id: `lib/${file}`, source: fs.readFileSync(path.join(libDirectory, file), 'utf8') }));
347
+
348
+ it('opens and closes every entry point the same way', () => {
349
+ expect(scripts).to.not.be.empty;
350
+ for (const { id, source } of scripts) {
351
+ expect(source.startsWith('#!/bin/bash\nset -euo pipefail\n'), id).to.equal(true);
352
+ expect(source, id).to.include('source "$SCRIPT_DIR/../lib/github-actions-logging.sh"');
353
+ expect(source.trimEnd().endsWith('main "$@"'), id).to.equal(true);
354
+ }
355
+ });
356
+
357
+ it('leaves the shared values to lib/config.sh, so no script carries a copy', () => {
358
+ // The engine root, the ingress node, the fleet's node names, the ssh key and the WordPress
359
+ // image were each pasted into a dozen scripts before this.
360
+ for (const { id, source } of scripts) {
361
+ expect(source, id).to.not.match(/^ENGINE_ROOT=/m);
362
+ expect(source, id).to.not.match(/^INGRESS_NODE=/m);
363
+ expect(source, id).to.not.include('/home/dd/engine');
364
+ expect(source, id).to.not.include('hp-envy');
365
+ expect(source, id).to.not.include('underpost/wp:');
366
+ }
367
+ });
368
+
369
+ it('states its deploy id once and reads it back from there', () => {
370
+ // A fleet-level script names no deploy of its own; one that names a deploy declares it.
371
+ for (const { id, source } of scripts.filter(({ source: text }) => /dd-[a-z0-9]/.test(text))) {
372
+ expect(source, id).to.match(/^DEPLOY_ID=dd-[a-z0-9-]+$/m);
373
+ const belowDeclaration = source.slice(source.indexOf('\n', source.search(/^DEPLOY_ID=/m)));
374
+ expect(belowDeclaration, id).to.not.match(/dd-[a-z0-9]/);
375
+ }
376
+ });
377
+
378
+ it('carries no library shebang and no trailing whitespace anywhere', () => {
379
+ for (const { id, source } of libraries) expect(source.startsWith('#!'), id).to.equal(false);
380
+ for (const { id, source } of [...scripts, ...libraries]) expect(source, id).to.not.match(/[ \t]+$/m);
381
+ });
382
+ });
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ import { expect } from 'chai';
4
+ import { shellExec } from '../../src/server/runtime/process.js';
5
+
6
+ describe('shell command child environment', () => {
7
+ it('passes an isolated environment value without adding it to command text', () => {
8
+ const secret = 'child-only-secret';
9
+ const output = shellExec('printf %s "$UNDERPOST_CHILD_ENV_FIXTURE"', {
10
+ stdout: true,
11
+ silent: true,
12
+ disableLog: true,
13
+ env: { ...process.env, UNDERPOST_CHILD_ENV_FIXTURE: secret },
14
+ });
15
+
16
+ expect(output).to.equal(secret);
17
+ });
18
+ });
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+
3
+ import { expect } from 'chai';
4
+ import fs from 'fs-extra';
5
+ // Named import: js-yaml's ESM build exports no default.
6
+ import { load } from 'js-yaml';
7
+ import { globSync } from 'node:fs';
8
+ import Underpost from '../../src/index.js';
9
+
10
+ const wrapped = (body) => `ci(package-pwa-microservices-template-ghpkg): ⚙️ ${body}`;
11
+
12
+ const workflows = () =>
13
+ globSync('.github/workflows/*.yml').map((file) => ({
14
+ file,
15
+ source: fs.readFileSync(file, 'utf8'),
16
+ document: load(fs.readFileSync(file, 'utf8')),
17
+ }));
18
+
19
+ const runScripts = (document) =>
20
+ Object.values(document?.jobs ?? {}).flatMap((job) => (job.steps ?? []).map((step) => step.run).filter(Boolean));
21
+
22
+ describe('propagation message', () => {
23
+ describe('resolvePropagationMessage', () => {
24
+ it('keeps every entry of a payload a previous hop committed', () => {
25
+ // The regression this guards: each hop commits the payload through `cmt`, which puts the
26
+ // newest entry on the subject line after the type prefix. Dropping the subject line — the
27
+ // obvious way to strip the prefix — silently propagated only the older entries onward.
28
+ const resolved = Underpost.repo.resolvePropagationMessage(
29
+ wrapped(
30
+ '[cli-repository] Add fastForwardEnginePair method\n[test] Fix coverall\n[package] Remove clean script',
31
+ ),
32
+ );
33
+ expect(resolved.split('\n')).to.deep.equal([
34
+ '[cli-repository] Add fastForwardEnginePair method',
35
+ '[test] Fix coverall',
36
+ '[package] Remove clean script',
37
+ ]);
38
+ });
39
+
40
+ it('carries characters a shell would have eaten', () => {
41
+ const resolved = Underpost.repo.resolvePropagationMessage(wrapped('[cli] Add $HOME lookup\n[test] Fix "quoted"'));
42
+ expect(resolved).to.equal('[cli] Add $HOME lookup\n[test] Fix "quoted"');
43
+ });
44
+
45
+ it('leaves an unwrapped payload alone and drops blank lines', () => {
46
+ expect(Underpost.repo.resolvePropagationMessage('[a] one\n\n [b] two ')).to.equal('[a] one\n[b] two');
47
+ });
48
+
49
+ it('reports nothing to propagate for an empty payload', () => {
50
+ expect(Underpost.repo.resolvePropagationMessage('')).to.equal('');
51
+ expect(Underpost.repo.resolvePropagationMessage(undefined)).to.equal('');
52
+ expect(Underpost.repo.resolvePropagationMessage(wrapped(''))).to.equal('');
53
+ });
54
+ });
55
+
56
+ describe('workflow chain', () => {
57
+ it('never expands a propagated message inside a run script', () => {
58
+ // `${{ }}` is substituted into the script before the shell parses it, so a multi-line
59
+ // payload holding `$` is corrupted and one holding `$(...)` executes. Every hop has to
60
+ // read it from the environment instead.
61
+ const offences = [];
62
+ for (const { file, document } of workflows())
63
+ for (const script of runScripts(document))
64
+ if (/\$\{\{[^}]*\.message[^}]*\}\}/.test(script)) offences.push(`${file}: message expanded in a run script`);
65
+ expect(offences, offences.join('\n')).to.deep.equal([]);
66
+ });
67
+
68
+ it('resolves every propagated commit message through the CLI', () => {
69
+ // One resolver for the whole chain: a workflow that rebuilds the payload with its own shell
70
+ // parsing is how the entries drifted apart between repositories.
71
+ const offences = [];
72
+ for (const { file, source } of workflows())
73
+ if (source.includes('LAST_COMMIT_MESSAGE=') && !source.includes('cmt --propagate-msg'))
74
+ offences.push(`${file}: builds a commit message without \`cmt --propagate-msg\``);
75
+ expect(offences, offences.join('\n')).to.deep.equal([]);
76
+ });
77
+ });
78
+ });
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ import { expect } from 'chai';
4
+ import { redeployPlanFactory } from '../../src/server/runtime/conf.js';
5
+
6
+ describe('a redeploy does not require the inactive colour to exist', () => {
7
+ it('restarts the target colour when it is already deployed', () => {
8
+ expect(redeployPlanFactory({ liveTraffic: 'blue', hasDeployment: (colour) => colour === 'green' })).to.deep.equal({
9
+ targetTraffic: 'green',
10
+ create: false,
11
+ });
12
+ });
13
+
14
+ it('creates the target colour when it was cleaned up by the previous cycle', () => {
15
+ expect(redeployPlanFactory({ liveTraffic: 'green', hasDeployment: (colour) => colour === 'green' })).to.deep.equal({
16
+ targetTraffic: 'blue',
17
+ create: true,
18
+ });
19
+ });
20
+
21
+ it('starts on blue and creates it when nothing is routed yet', () => {
22
+ expect(redeployPlanFactory({})).to.deep.equal({ targetTraffic: 'blue', create: true });
23
+ });
24
+ });