@underpostnet/cyberia 3.2.90 → 3.3.73

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 (490) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.example +16 -0
  3. package/.github/workflows/coverall.cyberia.ci.yml +107 -0
  4. package/.github/workflows/cyberia-client.cd.yml +6 -18
  5. package/.github/workflows/cyberia-server.cd.yml +6 -18
  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 +6 -78
  13. package/.github/workflows/engine-cyberia.ci.yml +4 -5
  14. package/.github/workflows/ghpkg.ci.yml +42 -21
  15. package/.github/workflows/hardhat.ci.yml +12 -11
  16. package/.github/workflows/npmpkg.ci.yml +17 -15
  17. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
  18. package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
  19. package/.github/workflows/release.cd.yml +2 -9
  20. package/.prettierignore +1 -0
  21. package/AGENTS.md +47 -0
  22. package/CHANGELOG.md +581 -2417
  23. package/CLI-HELP.md +416 -127
  24. package/Dockerfile +25 -9
  25. package/Dockerfile.dev +25 -9
  26. package/Dockerfile.test +4 -4
  27. package/README.md +21 -1
  28. package/bin/build.js +70 -43
  29. package/bin/build.template.js +8 -3
  30. package/bin/cyberia.js +372 -135
  31. package/bin/deploy.js +18 -104
  32. package/bin/index.js +372 -135
  33. package/compose.env +16 -0
  34. package/conf.js +93 -4
  35. package/deploy/cyberia-client/deploy.sh +47 -0
  36. package/deploy/cyberia-client/package.sh +18 -0
  37. package/deploy/cyberia-client/state.sh +14 -0
  38. package/deploy/cyberia-server/deploy.sh +47 -0
  39. package/deploy/cyberia-server/package.sh +18 -0
  40. package/deploy/cyberia-server/state.sh +14 -0
  41. package/deploy/dd-cyberia/deploy.sh +22 -0
  42. package/deploy/dd-cyberia/init.sh +55 -0
  43. package/deploy/dd-cyberia/package.sh +17 -0
  44. package/deploy/dd-cyberia/state.sh +14 -0
  45. package/deploy/dd-cyberia/sync-deploy.sh +116 -0
  46. package/deploy/lib/github-actions-logging.sh +594 -0
  47. package/deploy/lib/host.sh +185 -0
  48. package/deploy/lib/state.sh +62 -0
  49. package/deploy/pwa-microservices-template/deploy.sh +45 -0
  50. package/deploy/release/deploy.sh +32 -0
  51. package/deployment.yaml +7 -2
  52. package/docker-compose.yml +31 -37
  53. package/examples/static-page/README.md +19 -19
  54. package/examples/static-page/ssr-components/CustomPage.js +2 -2
  55. package/hardhat/hardhat.config.js +1 -6
  56. package/hardhat/package-lock.json +104 -43
  57. package/hardhat/package.json +3 -3
  58. package/manifests/alertmanager/deployment.yaml +81 -0
  59. package/manifests/blackbox-exporter/deployment.yaml +75 -0
  60. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +36 -7
  61. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +77 -0
  62. package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
  63. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  64. package/manifests/grafana/deployment.yaml +26 -7
  65. package/manifests/grafana/kustomization.yaml +1 -0
  66. package/manifests/grafana/service.yaml +6 -1
  67. package/manifests/mariadb/pv.yaml +1 -0
  68. package/manifests/mongodb/statefulset.yaml +9 -2
  69. package/manifests/mysql/pv-pvc.yaml +14 -1
  70. package/manifests/postgresql/pv.yaml +12 -1
  71. package/manifests/postgresql/pvc.yaml +1 -0
  72. package/manifests/prometheus/deployment.yaml +18 -22
  73. package/package.json +83 -10
  74. package/scripts/audit-selinux.sh +64 -0
  75. package/scripts/event-monitor.sh +56 -0
  76. package/scripts/gpu-diag.sh +0 -0
  77. package/scripts/ip-info.sh +0 -0
  78. package/scripts/k3s-node-setup.sh +30 -14
  79. package/scripts/kubeadm-node-setup.sh +40 -31
  80. package/scripts/lxd-vm-setup.sh +0 -0
  81. package/scripts/maas-nat-firewalld.sh +0 -0
  82. package/scripts/maas-setup.sh +8 -8
  83. package/scripts/nat-iptables.sh +2 -0
  84. package/scripts/rhel-grpc-setup.sh +0 -0
  85. package/scripts/rocky-kickstart.sh +25 -9
  86. package/scripts/selinux-normalize.sh +907 -0
  87. package/scripts/shutdown-machine.sh +843 -0
  88. package/scripts/test-monitor.sh +3 -3
  89. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +1 -1
  90. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +2 -2
  91. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +2 -2
  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 +1 -1
  99. package/src/api/cyberia-action/cyberia-action.service.js +2 -2
  100. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -1
  101. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +1 -1
  102. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +2 -2
  103. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
  104. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  105. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
  106. package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
  107. package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
  108. package/src/api/cyberia-entity/cyberia-entity.service.js +2 -2
  109. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +1 -1
  110. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +1 -1
  111. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +2 -2
  112. package/src/api/cyberia-instance/cyberia-fallback-capture.js +466 -0
  113. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +63 -0
  114. package/src/api/cyberia-instance/cyberia-fallback-world.js +16 -1
  115. package/src/api/cyberia-instance/cyberia-instance-map.service.js +1 -1
  116. package/src/api/cyberia-instance/cyberia-instance.controller.js +4 -1
  117. package/src/api/cyberia-instance/cyberia-instance.router.js +15 -2
  118. package/src/api/cyberia-instance/cyberia-instance.service.js +45 -2
  119. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
  120. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +1 -0
  121. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
  122. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +2 -2
  123. package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
  124. package/src/api/cyberia-map/cyberia-map.router.js +1 -1
  125. package/src/api/cyberia-map/cyberia-map.service.js +2 -2
  126. package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
  127. package/src/api/cyberia-quest/cyberia-quest.router.js +1 -1
  128. package/src/api/cyberia-quest/cyberia-quest.service.js +2 -2
  129. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
  130. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
  131. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
  132. package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
  133. package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
  134. package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
  135. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +16 -29
  136. package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
  137. package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
  138. package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
  139. package/src/api/default/default.controller.js +1 -1
  140. package/src/api/default/default.router.js +1 -1
  141. package/src/api/default/default.service.js +2 -2
  142. package/src/api/document/document.controller.js +1 -1
  143. package/src/api/document/document.router.js +1 -1
  144. package/src/api/document/document.service.js +2 -2
  145. package/src/api/file/file.controller.js +1 -1
  146. package/src/api/file/file.router.js +1 -1
  147. package/src/api/file/file.service.js +2 -2
  148. package/src/api/instance/instance.controller.js +1 -1
  149. package/src/api/instance/instance.router.js +1 -1
  150. package/src/api/instance/instance.service.js +2 -2
  151. package/src/api/ipfs/ipfs.controller.js +1 -1
  152. package/src/api/ipfs/ipfs.router.js +2 -2
  153. package/src/api/ipfs/ipfs.service.js +2 -2
  154. package/src/api/object-layer/object-layer.controller.js +1 -1
  155. package/src/api/object-layer/object-layer.model.js +204 -3
  156. package/src/api/object-layer/object-layer.router.js +2 -2
  157. package/src/api/object-layer/object-layer.service.js +4 -17
  158. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
  159. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
  160. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
  161. package/src/api/test/test.controller.js +1 -1
  162. package/src/api/test/test.service.js +1 -1
  163. package/src/api/user/guest.service.js +3 -3
  164. package/src/api/user/user.controller.js +1 -1
  165. package/src/api/user/user.router.js +2 -2
  166. package/src/api/user/user.service.js +4 -4
  167. package/src/api.js +5 -9
  168. package/src/cli/app.js +391 -0
  169. package/src/cli/baremetal.js +30 -36
  170. package/src/cli/client.js +306 -0
  171. package/src/cli/cloud-init.js +3 -3
  172. package/src/cli/cluster.js +242 -135
  173. package/src/cli/db.js +126 -67
  174. package/src/cli/deploy.js +158 -60
  175. package/src/cli/docker-compose.js +5 -5
  176. package/src/cli/domains.js +184 -0
  177. package/src/cli/dotenv-store.js +143 -0
  178. package/src/cli/event.js +2281 -0
  179. package/src/cli/fs.js +3 -3
  180. package/src/cli/host.js +672 -0
  181. package/src/cli/image.js +4 -7
  182. package/src/cli/index.js +426 -134
  183. package/src/cli/ipfs.js +34 -30
  184. package/src/cli/kickstart.js +1 -1
  185. package/src/cli/kubectl.js +218 -41
  186. package/src/cli/lxd.js +3 -3
  187. package/src/cli/monitor.js +1359 -26
  188. package/src/cli/package.js +90 -0
  189. package/src/cli/release.js +73 -34
  190. package/src/cli/repository.js +564 -367
  191. package/src/cli/run.js +362 -654
  192. package/src/cli/secrets.js +2158 -964
  193. package/src/cli/ssh.js +651 -331
  194. package/src/cli/state.js +394 -0
  195. package/src/cli/static.js +2 -2
  196. package/src/cli/system.js +26 -13
  197. package/src/cli/test.js +199 -125
  198. package/src/cli/vultr.js +623 -0
  199. package/src/cli/wireguard.js +2930 -0
  200. package/src/client/components/core/CalendarCore.js +53 -96
  201. package/src/client/components/core/Docs.js +258 -211
  202. package/src/client/components/core/FileExplorer.js +0 -21
  203. package/src/client/components/core/PanelForm.js +4 -4
  204. package/src/client/components/core/Repository.js +56 -0
  205. package/src/client/components/core/Translate.js +7 -0
  206. package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
  207. package/src/client/components/core/Wallet.js +0 -11
  208. package/src/client/components/core/Worker.js +7 -1
  209. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
  210. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +368 -0
  211. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +35 -1
  212. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +4 -0
  213. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
  214. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  215. package/src/client/components/default/AppShellDefault.js +1 -1
  216. package/src/client/components/default/SettingsDefault.js +2 -2
  217. package/src/client/components/itemledger/SettingsItemledger.js +2 -2
  218. package/src/client/components/underpost/AppShellUnderpost.js +3 -2
  219. package/src/client/components/underpost/SettingsUnderpost.js +2 -2
  220. package/src/client/public/cyberia-docs/ARCHITECTURE.md +17 -3
  221. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +32 -5
  222. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +1 -1
  223. package/src/client/public/cyberia-docs/CYBERIA.md +21 -1
  224. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
  225. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  226. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  227. package/src/client/public/default/android-chrome-144x144.png +0 -0
  228. package/src/client/public/default/android-chrome-192x192.png +0 -0
  229. package/src/client/public/default/android-chrome-256x256.png +0 -0
  230. package/src/client/public/default/android-chrome-36x36.png +0 -0
  231. package/src/client/public/default/android-chrome-384x384.png +0 -0
  232. package/src/client/public/default/android-chrome-48x48.png +0 -0
  233. package/src/client/public/default/android-chrome-512x512.png +0 -0
  234. package/src/client/public/default/android-chrome-72x72.png +0 -0
  235. package/src/client/public/default/android-chrome-96x96.png +0 -0
  236. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  237. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  238. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  239. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  240. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  241. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  242. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  243. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  244. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  245. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  246. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  247. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  248. package/src/client/public/default/apple-touch-icon.png +0 -0
  249. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  250. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  251. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  252. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  253. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  254. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  255. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  256. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  257. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  258. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  259. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  260. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  261. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  262. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  263. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  264. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  265. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  266. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  267. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  268. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  269. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  270. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  271. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  272. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  273. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  274. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  275. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  276. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  277. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  278. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  279. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  280. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  281. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  282. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  283. package/src/client/public/default/assets/background/white.jpg +0 -0
  284. package/src/client/public/default/browserconfig.xml +1 -1
  285. package/src/client/public/default/favicon-16x16.png +0 -0
  286. package/src/client/public/default/favicon-32x32.png +0 -0
  287. package/src/client/public/default/favicon-48x48.png +0 -0
  288. package/src/client/public/default/favicon.ico +0 -0
  289. package/src/client/public/default/manifest.webmanifest +5 -5
  290. package/src/client/public/default/mstile-144x144.png +0 -0
  291. package/src/client/public/default/mstile-150x150.png +0 -0
  292. package/src/client/public/default/mstile-310x150.png +0 -0
  293. package/src/client/public/default/mstile-310x310.png +0 -0
  294. package/src/client/public/default/mstile-70x70.png +0 -0
  295. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  296. package/src/client/public/default/yandex-browser-manifest.json +1 -1
  297. package/src/client/services/cyberia-instance/cyberia-instance.service.js +40 -0
  298. package/src/client/services/object-layer/object-layer.management.js +4 -4
  299. package/src/client/ssr/RootDocument.js +1 -1
  300. package/src/client/ssr/body/404.js +1 -1
  301. package/src/client/ssr/body/500.js +1 -1
  302. package/src/client/ssr/body/CacheControl.js +1 -1
  303. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
  304. package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
  305. package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
  306. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
  307. package/src/client/ssr/head/CryptokoynScripts.js +1 -1
  308. package/src/client/ssr/head/Css.js +1 -1
  309. package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
  310. package/src/client/ssr/head/DefaultScripts.js +1 -1
  311. package/src/client/ssr/head/ItemledgerScripts.js +1 -1
  312. package/src/client/ssr/head/Microdata.js +1 -1
  313. package/src/client/ssr/head/Production.js +1 -1
  314. package/src/client/ssr/head/Pwa.js +1 -1
  315. package/src/client/ssr/head/PwaItemledger.js +1 -1
  316. package/src/client/ssr/head/Seo.js +1 -1
  317. package/src/client/ssr/head/UnderpostScripts.js +1 -1
  318. package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
  319. package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
  320. package/src/client/ssr/views/Cyberia404.js +7 -5
  321. package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
  322. package/src/client/ssr/views/Maintenance.js +1 -1
  323. package/src/client/ssr/views/NoNetworkConnection.js +1 -1
  324. package/src/client/ssr/views/Test.js +1 -1
  325. package/src/client/sw/core.sw.js +20 -14
  326. package/src/client-builder/client-build-docs.js +61 -46
  327. package/src/client-builder/client-build-live.js +2 -2
  328. package/src/client-builder/client-build.js +58 -44
  329. package/src/client-builder/client-dev-server.js +3 -3
  330. package/src/client-builder/client-icons.js +2 -2
  331. package/src/client-builder/ssr.js +3 -3
  332. package/src/client.build.js +3 -6
  333. package/src/client.dev.js +3 -6
  334. package/src/db/DataBaseProvider.js +186 -3
  335. package/src/db/mariadb/MariaDB.js +3 -2
  336. package/src/db/mongo/MongoBootstrap.js +273 -101
  337. package/src/db/mongo/MongooseDB.js +11 -8
  338. package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
  339. package/src/grpc/cyberia/grpc-server.js +1 -1
  340. package/src/index.js +103 -23
  341. package/src/mailer/EmailRender.js +2 -2
  342. package/src/mailer/MailerInterceptor.js +87 -0
  343. package/src/mailer/MailerProvider.js +50 -6
  344. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +1 -1
  345. package/src/projects/cyberia/besu-genesis-generator.js +3 -3
  346. package/src/projects/cyberia/catalog-cyberia.js +54 -6
  347. package/src/projects/cyberia/gemini-client.js +1 -1
  348. package/src/projects/cyberia/generate-saga.js +15 -24
  349. package/src/projects/cyberia/hot-reload-trigger.js +1 -1
  350. package/src/projects/cyberia/instance-data.js +22 -3
  351. package/src/projects/cyberia/ipfs-client.js +4 -4
  352. package/src/projects/cyberia/map-preview-generator.js +1 -1
  353. package/src/projects/cyberia/object-layer.js +12 -22
  354. package/src/projects/cyberia/semantic-layer-generator.js +1 -1
  355. package/src/projects/underpost/catalog-underpost.js +13 -2
  356. package/src/proxy.js +4 -7
  357. package/src/runtime/cyberia-client/Dockerfile +9 -4
  358. package/src/runtime/cyberia-client/Dockerfile.dev +8 -2
  359. package/src/runtime/cyberia-server/Dockerfile +9 -4
  360. package/src/runtime/cyberia-server/Dockerfile.dev +8 -2
  361. package/src/runtime/engine-cyberia/Dockerfile +25 -9
  362. package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
  363. package/src/runtime/engine-cyberia/Dockerfile.test +4 -4
  364. package/src/runtime/engine-cyberia/compose.env +16 -0
  365. package/src/runtime/engine-cyberia/docker-compose.yml +31 -37
  366. package/src/runtime/express/Express.js +32 -27
  367. package/src/runtime/lampp/Lampp.js +22 -70
  368. package/src/runtime/nginx/Nginx.js +1 -1
  369. package/src/runtime/wp/Wp.js +16 -48
  370. package/src/server/{catalog.js → build/catalog.js} +15 -5
  371. package/src/server/build/coverage.js +117 -0
  372. package/src/server/build/execution.js +402 -0
  373. package/src/server/build/package.js +465 -0
  374. package/src/server/build/testing.js +528 -0
  375. package/src/server/{dns.js → network/dns.js} +241 -14
  376. package/src/server/network/forward-proxy.js +541 -0
  377. package/src/server/{middlewares.js → network/middlewares.js} +59 -4
  378. package/src/server/network/node-capability.js +98 -0
  379. package/src/server/{peer.js → network/peer.js} +3 -3
  380. package/src/server/{proxy.js → network/proxy.js} +7 -13
  381. package/src/server/network/router.js +356 -0
  382. package/src/server/{tls.js → network/tls.js} +2 -2
  383. package/src/server/network/underpost-compression.js +186 -0
  384. package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +26 -16
  385. package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +19 -3
  386. package/src/server/{cri.js → ops/cri.js} +2 -2
  387. package/src/server/ops/cron.js +949 -0
  388. package/src/server/ops/event-notification.js +284 -0
  389. package/src/server/{logger.js → ops/logger.js} +108 -23
  390. package/src/server/ops/monitoring.js +1724 -0
  391. package/src/server/ops/systemd.js +259 -0
  392. package/src/server/{conf.js → runtime/conf.js} +521 -520
  393. package/src/server/runtime/config-scope.js +215 -0
  394. package/src/server/runtime/environment.js +149 -0
  395. package/src/server/{process.js → runtime/process.js} +153 -13
  396. package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
  397. package/src/server/{runtime.js → runtime/runtime.js} +12 -8
  398. package/src/server/{start.js → runtime/start.js} +128 -39
  399. package/src/server/{auth.js → security/auth.js} +7 -8
  400. package/src/server/security/container-storage.js +382 -0
  401. package/src/server/{crypto.js → security/crypto.js} +1 -1
  402. package/src/server/security/selinux.js +185 -0
  403. package/src/server/{backup.js → storage/backup.js} +17 -8
  404. package/src/server/{data-query.js → storage/data-query.js} +1 -1
  405. package/src/server/{downloader.js → storage/downloader.js} +2 -2
  406. package/src/server/storage/repository.js +68 -0
  407. package/src/server.js +4 -7
  408. package/src/ws/IoInterface.js +1 -1
  409. package/src/ws/IoServer.js +1 -1
  410. package/src/ws/core/core.ws.connection.js +1 -1
  411. package/src/ws/core/core.ws.emit.js +1 -1
  412. package/src/ws/core/core.ws.server.js +1 -1
  413. package/src/ws/default/default.ws.connection.js +1 -1
  414. package/src/ws/default/default.ws.emit.js +1 -1
  415. package/src/ws/default/default.ws.server.js +1 -1
  416. package/test/e2e/event-e2e-public-ingress-down.js +45 -0
  417. package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
  418. package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
  419. package/test/{api.test.js → integration/app/api.test.js} +33 -14
  420. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +30 -0
  421. package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +2 -1
  422. package/test/integration/app/cyberia/cyberia-load.test.js +467 -0
  423. package/test/integration/app/cyberia/fallback-world-capture.test.js +253 -0
  424. package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
  425. package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
  426. package/test/integration/infra/1-security/config-scope.test.js +305 -0
  427. package/test/integration/infra/1-security/container-storage.test.js +356 -0
  428. package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
  429. package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
  430. package/test/integration/infra/1-security/selinux.test.js +69 -0
  431. package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
  432. package/test/integration/infra/1-security/systemd-service.test.js +70 -0
  433. package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
  434. package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
  435. package/test/integration/infra/2-network/node-capability.test.js +182 -0
  436. package/test/integration/infra/2-network/wireguard-cli.test.js +1163 -0
  437. package/test/integration/infra/2-network/wireguard-edge.test.js +1731 -0
  438. package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
  439. package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
  440. package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
  441. package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
  442. package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
  443. package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
  444. package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +47 -6
  445. package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +56 -4
  446. package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
  447. package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
  448. package/test/integration/infra/5-observability/event-notification.test.js +197 -0
  449. package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
  450. package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
  451. package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
  452. package/test/support/shell-harness.js +73 -0
  453. package/test/unit/build-template.test.js +97 -0
  454. package/test/unit/build-workflow-offline.test.js +132 -0
  455. package/test/unit/catalog.test.js +71 -0
  456. package/test/unit/client-build-docs.test.js +334 -0
  457. package/test/unit/conf-loading.test.js +303 -0
  458. package/test/unit/conf-resolution.test.js +979 -0
  459. package/test/unit/coverage-artifact.test.js +130 -0
  460. package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
  461. package/test/unit/deploy-env-resolution.test.js +38 -0
  462. package/test/unit/deploy-log-table.test.js +447 -0
  463. package/test/unit/execution-profiles.test.js +219 -0
  464. package/test/unit/fleet-sync-source.test.js +149 -0
  465. package/test/unit/logger-redaction.test.js +112 -0
  466. package/test/unit/package.test.js +403 -0
  467. package/test/unit/prepare-host.test.js +299 -0
  468. package/test/unit/process-environment.test.js +18 -0
  469. package/test/unit/propagation-message.test.js +78 -0
  470. package/test/unit/redeploy-plan.test.js +24 -0
  471. package/test/unit/release-bump.test.js +474 -0
  472. package/test/unit/ssh-output-redaction.test.js +28 -0
  473. package/test/unit/start-options.test.js +46 -0
  474. package/test/unit/test-tiers.test.js +193 -0
  475. package/vitest.config.js +58 -0
  476. package/.nycrc +0 -9
  477. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
  478. package/manifests/mongodb/pv-pvc.yaml +0 -59
  479. package/scripts/link-local-underpost-cli.sh +0 -6
  480. package/src/api/user/user.build.js +0 -16
  481. package/src/cli/env.js +0 -177
  482. package/src/client/components/core/Blockchain.js +0 -41
  483. package/src/client/components/core/Blog.js +0 -9
  484. package/src/client/components/core/KeyboardAvoidance.js +0 -145
  485. package/src/client/public/default/assets/background/dark.jpg +0 -0
  486. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  487. package/src/client/public/default/assets/background/white0.jpg +0 -0
  488. package/src/client/public/doc/favicon.ico +0 -0
  489. package/src/client/public/doc/sitemap +0 -148
  490. package/src/server/cron.js +0 -476
@@ -4,18 +4,31 @@
4
4
  * @namespace UnderpostCluster
5
5
  */
6
6
 
7
- import { clusterTypeFactory, gatewayApiEnabledFactory, getNpmRootPath, resolveReplicaCount } from '../server/conf.js';
8
- import { loggerFactory } from '../server/logger.js';
9
- import { shellExec } from '../server/process.js';
10
- import { crictlCommandFactory, resolveCriSocket } from '../server/cri.js';
11
- import { UNDERPOST_GATEWAY, seedDefaultStatusPage } from '../server/underpost-gateway.js';
7
+ import { clusterTypeFactory, gatewayApiEnabledFactory, resolveReplicaCount } from '../server/runtime/conf.js';
8
+ import { getNpmRootPath, HOST_VOLUME_ROOT } from '../server/runtime/environment.js';
9
+ import { loggerFactory } from '../server/ops/logger.js';
10
+ import { shellExec } from '../server/runtime/process.js';
11
+ import { crictlCommandFactory, resolveCriSocket } from '../server/ops/cri.js';
12
+ import {
13
+ runSELinuxCommands,
14
+ selinuxEnforcingCommandsFactory,
15
+ selinuxPackagesCommandFactory,
16
+ selinuxRestoreconCommandFactory,
17
+ } from '../server/security/selinux.js';
18
+ import ContainerStorageService, {
19
+ containerStorageRootsFactory,
20
+ ensureContainerStorage,
21
+ localPathConfigCommandFactory,
22
+ localPathRootsFactory,
23
+ } from '../server/security/container-storage.js';
24
+ import { UNDERPOST_GATEWAY, seedDefaultStatusPage } from '../server/network/underpost-gateway.js';
12
25
  import {
13
26
  UNDERPOST_INGRESS,
14
27
  gatewayBackendFactory,
15
28
  underpostIngressConfFactory,
16
29
  underpostIngressHostMapFactory,
17
30
  underpostIngressManifestsFactory,
18
- } from '../server/underpost-ingress.js';
31
+ } from '../server/network/underpost-ingress.js';
19
32
  import { MONGODB_DEFAULT_REPLICA_COUNT } from '../db/mongo/MongooseDB.js';
20
33
  import { MongoBootstrap } from '../db/mongo/MongoBootstrap.js';
21
34
  import os from 'os';
@@ -24,17 +37,32 @@ import Underpost from '../index.js';
24
37
 
25
38
  const logger = loggerFactory(import.meta);
26
39
 
27
- // Pinned Gateway API control plane. The CRD release and the implementation are
28
- // upgraded together, and the pairing is not free choice: Envoy Gateway builds
29
- // against one `sigs.k8s.io/gateway-api` version (v1.8.3 -> v1.5.1) and reads
30
- // fields the older CRD schemas do not define, which the API
31
- // server silently strips. Check the implementation's go.mod before moving
32
- // either pin.
33
40
  const GATEWAY_API_RELEASE = 'v1.5.1';
34
41
  const ENVOY_GATEWAY_VERSION = 'v1.8.3';
35
- // Namespace the upstream Contour render deploys into, and the only one where an
36
- // `app=envoy` selector resolves to its DaemonSet.
37
42
  const CONTOUR_NAMESPACE = 'projectcontour';
43
+ const LOCAL_PATH_PROVISIONER_MANIFEST =
44
+ 'https://cdn.jsdelivr.net/gh/rancher/local-path-provisioner@master/deploy/local-path-storage.yaml';
45
+ const K3S_SELINUX_PATHS = [
46
+ '/usr/local/bin/k3s',
47
+ '/etc/rancher',
48
+ '/var/lib/rancher',
49
+ '/var/lib/kubelet',
50
+ '/var/lib/cni',
51
+ ];
52
+
53
+ // `semanage` and `restorecon` come from separate packages and neither is guaranteed on a minimal
54
+ // Rocky install. Installed before anything tries to register a mapping, because a missing
55
+ // `semanage` would otherwise turn every persistent labeling command into a hard failure.
56
+ const ensureSELinuxTooling = () =>
57
+ shellExec(
58
+ `if [ -f /etc/redhat-release ] && command -v dnf >/dev/null 2>&1 && { ! command -v restorecon >/dev/null 2>&1 || ! command -v semanage >/dev/null 2>&1; }; then ${selinuxPackagesCommandFactory()}; fi`,
59
+ );
60
+
61
+ const enforceSELinux = (paths = []) => {
62
+ ensureSELinuxTooling();
63
+ const commands = selinuxEnforcingCommandsFactory({ restorePaths: paths });
64
+ runSELinuxCommands(commands, { execute: shellExec });
65
+ };
38
66
 
39
67
  /**
40
68
  * @class UnderpostCluster
@@ -90,9 +118,10 @@ class UnderpostCluster {
90
118
  * @param {boolean} [options.nodePort=false] - Expose enabled ready services (e.g. MongoDB 4.4, Valkey)
91
119
  * to the host/public network via their NodePort Service manifest. The node port value lives directly
92
120
  * in each manifest (mongodb-4.4/mongodb-nodeport.yaml, valkey/valkey-nodeport.yaml).
93
- * @param {string} [options.nodeSelector=''] - Pin the just-deployed StatefulSet (MongoDB 4.4 / Valkey)
94
- * to a specific Kubernetes node by name (e.g. 'localhost.localdomain'). Applied via a
95
- * `kubernetes.io/hostname` nodeSelector patch once the workload reports Ready.
121
+ * @param {string} [options.nodeName=''] - Pin the just-deployed StatefulSet (MongoDB 4.4 / Valkey)
122
+ * and the observability workloads to a specific Kubernetes node by name (e.g.
123
+ * 'localhost.localdomain'). Applied via a `kubernetes.io/hostname` nodeSelector patch once
124
+ * the workload reports Ready.
96
125
  * @memberof UnderpostCluster
97
126
  */
98
127
  async init(
@@ -132,7 +161,7 @@ class UnderpostCluster {
132
161
  hosts: '',
133
162
  replicas: '',
134
163
  nodePort: false,
135
- nodeSelector: '',
164
+ nodeName: '',
136
165
  },
137
166
  ) {
138
167
  if (options.initHost) return Underpost.cluster.initHost();
@@ -210,6 +239,12 @@ class UnderpostCluster {
210
239
  logger.info(
211
240
  `Detected existing ${runtime.type} cluster (${runtime.ready ? 'Ready' : 'NotReady'}); skipping initialization.`,
212
241
  );
242
+ // Initialization is skipped, but the labeling invariant is not: the node directories
243
+ // backing PersistentVolumes outlive the bring-up that created them, and a policy update,
244
+ // a full relabel or a directory recreated by kubelet resets them to the policy default.
245
+ // Re-asserting it costs a `semanage` lookup and a `restorecon` pass, mutates no workload,
246
+ // and never changes SELinux mode.
247
+ Underpost.cluster.ensureContainerStorageContexts();
213
248
  }
214
249
 
215
250
  // --- Kubeadm/Kind/K3s Cluster Initialization ---
@@ -227,8 +262,11 @@ class UnderpostCluster {
227
262
  // balancer. The platform exposes services explicitly via Project
228
263
  // Contour / Envoy and NodePort services (see --node-port); leaving the
229
264
  // K3s built-ins enabled would bind the same host ports and conflict.
230
- shellExec(`curl -sfL https://get.k3s.io | sh -s - --disable=traefik --disable=servicelb`);
265
+ shellExec(
266
+ `curl -sfL https://get.k3s.io | sh -s - $(if command -v selinuxenabled >/dev/null 2>&1 && selinuxenabled; then printf '%s' '--selinux'; fi) --disable=traefik --disable=servicelb`,
267
+ );
231
268
  logger.info('K3s installation completed.');
269
+ runSELinuxCommands([selinuxRestoreconCommandFactory(K3S_SELINUX_PATHS)], { execute: shellExec });
232
270
 
233
271
  Underpost.cluster.chown('k3s');
234
272
 
@@ -240,6 +278,7 @@ class UnderpostCluster {
240
278
  } else if (options.kubeadm) {
241
279
  Underpost.cluster.config();
242
280
  Underpost.cluster.natSetup({ underpostRoot });
281
+ Underpost.cluster.ensureContainerStorageContexts({ controlPlane: true });
243
282
  logger.info('Initializing Kubeadm control plane...');
244
283
  // Set default values if not provided
245
284
  const podNetworkCidr = options.podNetworkCidr || '192.168.0.0/16';
@@ -267,11 +306,13 @@ class UnderpostCluster {
267
306
  // Untaint control plane node to allow scheduling pods
268
307
  const nodeName = os.hostname();
269
308
  shellExec(`kubectl taint nodes ${nodeName} node-role.kubernetes.io/control-plane:NoSchedule-`);
270
- // Install local-path-provisioner for dynamic PVCs
271
- logger.info('Installing local-path-provisioner...');
272
- shellExec(
273
- `kubectl apply -f https://cdn.jsdelivr.net/gh/rancher/local-path-provisioner@master/deploy/local-path-storage.yaml`,
274
- );
309
+ // The CNI lands asynchronously, and the node stays NotReady until it
310
+ // does. Block here so the deploy steps that follow (MongoDB and the
311
+ // application workloads) schedule onto a node that can actually run
312
+ // them, instead of racing the Calico rollout.
313
+ logger.info('Waiting for the node to become Ready (Calico rollout)...');
314
+ shellExec(`kubectl wait --for=condition=Ready node --all --timeout=300s`);
315
+ Underpost.cluster.ensureLocalPathProvisioner();
275
316
  } else {
276
317
  Underpost.cluster.config();
277
318
  Underpost.cluster.natSetup({ underpostRoot });
@@ -322,36 +363,19 @@ class UnderpostCluster {
322
363
  shellExec(`node ${underpostRoot}/bin/deploy kubeflow-spark-operator${options.kubeadm ? ' kubeadm' : ''}`);
323
364
  }
324
365
 
325
- if (options.grafana) {
326
- shellExec(`kubectl delete deployment grafana -n ${options.namespace} --ignore-not-found`);
327
- shellExec(`kubectl apply -k ${underpostRoot}/manifests/grafana -n ${options.namespace}`);
328
- const yaml = `${fs
329
- .readFileSync(`${underpostRoot}/manifests/grafana/deployment.yaml`, 'utf8')
330
- .replace('{{GF_SERVER_ROOT_URL}}', options.hosts.split(',')[0])}`;
331
- console.log(yaml);
332
- shellExec(`kubectl apply -f - -n ${options.namespace} <<'EOF'
333
- ${yaml}
334
- EOF
335
- `);
336
- }
337
-
338
- if (options.prom) {
339
- shellExec(`kubectl delete deployment prometheus --ignore-not-found`);
340
- shellExec(`kubectl delete configmap prometheus-config --ignore-not-found`);
341
- shellExec(`kubectl delete service prometheus --ignore-not-found`);
342
- // Prometheus server host: http://<prometheus-cluster-ip>:9090
343
- const yaml = `${fs.readFileSync(`${underpostRoot}/manifests/prometheus/deployment.yaml`, 'utf8').replace(
344
- '- targets: []',
345
- `- targets: [${options.prom
346
- .split(',')
347
- .map((host) => `'${host}'`)
348
- .join(',')}]`,
349
- )}`;
350
- console.log(yaml);
351
- shellExec(`kubectl apply -f - -n ${options.namespace} <<'EOF'
352
- ${yaml}
353
- EOF
354
- `);
366
+ // Grafana and Prometheus are two faces of one stack, so either flag
367
+ // converges the whole of it — including Alertmanager and the Blackbox
368
+ // Exporter, which the alert rules and probes are useless without. The
369
+ // scrape configuration is rendered from the deploy configuration and the
370
+ // event registry by the monitor CLI; `--prom [hosts]` only adds hosts
371
+ // that configuration does not name.
372
+ if (options.grafana || options.prom) {
373
+ await Underpost.monitor.deployObservability({
374
+ namespace: options.namespace,
375
+ dev: options.dev,
376
+ nodeName: options.nodeName,
377
+ hosts: typeof options.prom === 'string' ? options.prom : options.hosts,
378
+ });
355
379
  }
356
380
 
357
381
  if (options.valkey) {
@@ -363,11 +387,11 @@ EOF
363
387
  // The node port (32079) is set directly in the manifest.
364
388
  if (valkeyReady && options.nodePort)
365
389
  shellExec(`kubectl apply -f ${underpostRoot}/manifests/valkey/valkey-nodeport.yaml -n ${options.namespace}`);
366
- if (valkeyReady && options.nodeSelector)
390
+ if (valkeyReady && options.nodeName)
367
391
  Underpost.cluster.pinToNode({
368
392
  name: 'valkey-service',
369
393
  namespace: options.namespace,
370
- node: options.nodeSelector,
394
+ node: options.nodeName,
371
395
  });
372
396
  if (valkeyReady && serviceHost)
373
397
  Underpost.cluster.syncServiceConnectionEnv({
@@ -384,32 +408,32 @@ EOF
384
408
  // Encrypted store first; when no manifest exists the secret is seeded from its origin
385
409
  // seed path below. A manifest that exists but fails validation raises rather than
386
410
  // silently seeding stale credentials.
387
- if (!Underpost.secret.sops.applyIfPresent('mariadb-secret', options.namespace))
388
- shellExec(
389
- `sudo kubectl create secret generic mariadb-secret --from-file=username=/home/dd/engine/engine-private/mariadb-username --from-file=password=/home/dd/engine/engine-private/mariadb-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
390
- );
411
+ if (!Underpost.secret.applyIfPresent('mariadb-secret', options.namespace))
412
+ Underpost.secret.applyFromOriginSeed('mariadb-secret', options.namespace);
391
413
  shellExec(`kubectl delete statefulset mariadb-statefulset -n ${options.namespace} --ignore-not-found`);
392
414
 
393
415
  if (options.pullImage) Underpost.cluster.pullImage('mariadb:latest', options);
416
+ // The StatefulSet's volumeClaimTemplate is dynamically provisioned, so the data lands in
417
+ // a subdirectory of the local-path root and inherits that root's label.
418
+ Underpost.cluster.ensureContainerStorageContexts({ extraPaths: ['/data/mariadb'] });
394
419
  shellExec(`kubectl apply -f ${underpostRoot}/manifests/mariadb/storage-class.yaml -n ${options.namespace}`);
395
420
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/mariadb -n ${options.namespace}`);
396
421
  }
397
422
  if (options.mysql) {
398
- if (!Underpost.secret.sops.applyIfPresent('mysql-secret', options.namespace))
399
- shellExec(
400
- `sudo kubectl create secret generic mysql-secret --from-file=username=/home/dd/engine/engine-private/mysql-username --from-file=password=/home/dd/engine/engine-private/mysql-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
401
- );
402
- shellExec(`sudo mkdir -p /mnt/data`);
403
- shellExec(`sudo chmod 777 /mnt/data`);
404
- shellExec(`sudo chown -R $(whoami):$(whoami) /mnt/data`);
423
+ if (!Underpost.secret.applyIfPresent('mysql-secret', options.namespace))
424
+ Underpost.secret.applyFromOriginSeed('mysql-secret', options.namespace);
425
+ // The data directory is prepared, not opened up: a world-writable host path plus the
426
+ // operator's ownership was standing in for the SELinux label the container actually
427
+ // needed. `container_file_t` is what lets mysqld write here; 0755 root-owned is enough,
428
+ // because the image's entrypoint chowns the tree to its own uid on first start.
429
+ Underpost.cluster.ensureContainerStorageContexts({ extraPaths: ['/data/mysql'] });
405
430
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/mysql -n ${options.namespace}`);
406
431
  }
407
432
  if (options.postgresql) {
408
433
  if (options.pullImage) Underpost.cluster.pullImage('postgres:latest', options);
409
- if (!Underpost.secret.sops.applyIfPresent('postgres-secret', options.namespace))
410
- shellExec(
411
- `sudo kubectl create secret generic postgres-secret --from-file=password=/home/dd/engine/engine-private/postgresql-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
412
- );
434
+ if (!Underpost.secret.applyIfPresent('postgres-secret', options.namespace))
435
+ Underpost.secret.applyFromOriginSeed('postgres-secret', options.namespace);
436
+ Underpost.cluster.ensureContainerStorageContexts({ extraPaths: ['/data/postgresql'] });
413
437
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/postgresql -n ${options.namespace}`);
414
438
  }
415
439
  if (options.mongodb4) {
@@ -453,11 +477,11 @@ EOF
453
477
  shellExec(
454
478
  `kubectl apply -f ${underpostRoot}/manifests/mongodb-4.4/mongodb-nodeport.yaml -n ${options.namespace}`,
455
479
  );
456
- if (options.nodeSelector)
480
+ if (options.nodeName)
457
481
  Underpost.cluster.pinToNode({
458
482
  name: statefulSetName,
459
483
  namespace: options.namespace,
460
- node: options.nodeSelector,
484
+ node: options.nodeName,
461
485
  });
462
486
  if (serviceHost)
463
487
  Underpost.cluster.syncServiceConnectionEnv({
@@ -691,13 +715,107 @@ EOF
691
715
  return { type, ready, nodes };
692
716
  },
693
717
 
718
+ /**
719
+ * @method discoverLocalPathRoots
720
+ * @description Reads the node directories the local-path provisioner writes into, from its
721
+ * live ConfigMap. Every dynamically provisioned claim in this platform (Grafana, Prometheus,
722
+ * Alertmanager, IPFS, MariaDB) becomes a subdirectory of one of them, so the root has to be
723
+ * discovered rather than assumed — a wrong guess leaves those claims carrying the `/opt`
724
+ * policy default (`usr_t`), which no `container_t` pod can write.
725
+ * @returns {string[]} Configured node paths; empty when no provisioner is reachable.
726
+ * @memberof UnderpostCluster
727
+ */
728
+ discoverLocalPathRoots() {
729
+ for (const namespace of ContainerStorageService.LOCAL_PATH_NAMESPACES) {
730
+ const config = `${
731
+ shellExec(localPathConfigCommandFactory({ namespace }), {
732
+ stdout: true,
733
+ silent: true,
734
+ silentOnError: true,
735
+ disableLog: true,
736
+ }) || ''
737
+ }`.trim();
738
+ const roots = localPathRootsFactory(config);
739
+ if (roots.length > 0) return roots;
740
+ }
741
+ return [];
742
+ },
743
+
744
+ /**
745
+ * @method ensureContainerStorageContexts
746
+ * @description Makes SELinux compatibility an invariant of the storage lifecycle rather than
747
+ * a one-time bring-up step: creates the node directories this platform hands to containers
748
+ * (only when missing, so live data trees keep their ownership and mode), registers the
749
+ * persistent `container_file_t` mapping for each, and restores the trees.
750
+ *
751
+ * Idempotent and safe to call on every deploy. Never relabels paths outside the discovered
752
+ * set, and never changes SELinux mode.
753
+ * @param {object} [options]
754
+ * @param {boolean} [options.controlPlane=false] - Also cover the kubeadm control-plane trees
755
+ * bind-mounted into containers (`/etc/kubernetes`, `/var/lib/etcd`, `/var/lib/calico`).
756
+ * @param {string[]} [options.extraPaths=[]] - Additional node directories to include.
757
+ * @returns {string[]} The roots that were prepared.
758
+ * @memberof UnderpostCluster
759
+ */
760
+ ensureContainerStorageContexts(options = {}) {
761
+ const roots = containerStorageRootsFactory({
762
+ localPathRoots: Underpost.cluster.discoverLocalPathRoots(),
763
+ extraPaths: options.extraPaths || [],
764
+ controlPlane: options.controlPlane === true,
765
+ });
766
+ logger.info('Preparing container-mounted host storage', { roots });
767
+ // HOST_VOLUME_ROOT is the one root the deploy flow writes into unprivileged (it lives in the
768
+ // operator's home tree and receives each hostPath volume's contents), so it is created owned
769
+ // by the invoking user. The rest stay root-owned: their subdirectories are chowned by each
770
+ // workload's own init container, which is the single owner of that concern.
771
+ const { uid, gid } = os.userInfo();
772
+ const operatorRoots = roots.filter((root) => root === HOST_VOLUME_ROOT);
773
+ const systemRoots = roots.filter((root) => root !== HOST_VOLUME_ROOT);
774
+ if (systemRoots.length > 0) ensureContainerStorage(systemRoots, { execute: shellExec });
775
+ if (operatorRoots.length > 0)
776
+ ensureContainerStorage(operatorRoots, { execute: shellExec, owner: `${uid}:${gid}` });
777
+ return roots;
778
+ },
779
+
780
+ /**
781
+ * @method ensureLocalPathProvisioner
782
+ * @description Ensures dynamic local-path PVC provisioning is available.
783
+ * @returns {string} Namespace containing the ready provisioner.
784
+ * @memberof UnderpostCluster
785
+ */
786
+ ensureLocalPathProvisioner() {
787
+ const locations = ['local-path-storage', 'kube-system'];
788
+ let namespace = locations.find((candidate) =>
789
+ `${
790
+ shellExec(`kubectl get deployment local-path-provisioner -n ${candidate} -o name`, {
791
+ stdout: true,
792
+ silent: true,
793
+ silentOnError: true,
794
+ disableLog: true,
795
+ }) || ''
796
+ }`.trim(),
797
+ );
798
+
799
+ if (!namespace) {
800
+ logger.info('Installing local-path-provisioner');
801
+ shellExec(`kubectl apply -f ${LOCAL_PATH_PROVISIONER_MANIFEST}`);
802
+ namespace = 'local-path-storage';
803
+ }
804
+ shellExec(`kubectl rollout status deployment/local-path-provisioner -n ${namespace} --timeout=5m`);
805
+ // The provisioner's root exists (or is about to) only now, and its helper pod creates each
806
+ // claim directory by inheriting that root's label. Prepare it here so the first dynamically
807
+ // provisioned claim already lands on `container_file_t` instead of `/opt`'s `usr_t`.
808
+ Underpost.cluster.ensureContainerStorageContexts();
809
+ return namespace;
810
+ },
811
+
694
812
  /**
695
813
  * @method pinToNode
696
814
  * @description Pins a workload to a specific Kubernetes node by patching its
697
815
  * pod template with a `kubernetes.io/hostname` nodeSelector. General-purpose;
698
- * currently used to place the MongoDB 4.4 / Valkey StatefulSets on a chosen
699
- * node (`--node-selector`). The patch triggers a rolling reschedule onto the
700
- * target node.
816
+ * currently used to place the MongoDB 4.4 / Valkey StatefulSets and the
817
+ * observability Deployments on a chosen node (`--node-name`). The patch
818
+ * triggers a rolling reschedule onto the target node.
701
819
  * @param {object} params
702
820
  * @param {string} [params.kind='statefulset'] - Workload kind to patch.
703
821
  * @param {string} params.name - Workload name.
@@ -1198,11 +1316,13 @@ EOF
1198
1316
  }
1199
1317
  }
1200
1318
  const liveConf = canHotReload
1201
- ? `${execIngress('cat /tmp/nginx.conf', {
1202
- stdout: true,
1203
- silent: true,
1204
- silentOnError: true,
1205
- }) || ''}`
1319
+ ? `${
1320
+ execIngress('cat /tmp/nginx.conf', {
1321
+ stdout: true,
1322
+ silent: true,
1323
+ silentOnError: true,
1324
+ }) || ''
1325
+ }`
1206
1326
  : '';
1207
1327
  const shouldHotReload = canHotReload && liveConf.trimEnd() !== `${conf}`.trimEnd();
1208
1328
 
@@ -1222,10 +1342,10 @@ EOF
1222
1342
  { silent: true },
1223
1343
  );
1224
1344
  } catch (error) {
1225
- execIngress(
1226
- `sh -c 'cp /tmp/nginx.previous.conf /tmp/nginx.conf && nginx -s reload -c /tmp/nginx.conf'`,
1227
- { silent: true, silentOnError: true },
1228
- );
1345
+ execIngress(`sh -c 'cp /tmp/nginx.previous.conf /tmp/nginx.conf && nginx -s reload -c /tmp/nginx.conf'`, {
1346
+ silent: true,
1347
+ silentOnError: true,
1348
+ });
1229
1349
  throw error;
1230
1350
  }
1231
1351
  }
@@ -1241,10 +1361,10 @@ EOF
1241
1361
  `);
1242
1362
  } catch (error) {
1243
1363
  if (shouldHotReload)
1244
- execIngress(
1245
- `sh -c 'cp /tmp/nginx.previous.conf /tmp/nginx.conf && nginx -s reload -c /tmp/nginx.conf'`,
1246
- { silent: true, silentOnError: true },
1247
- );
1364
+ execIngress(`sh -c 'cp /tmp/nginx.previous.conf /tmp/nginx.conf && nginx -s reload -c /tmp/nginx.conf'`, {
1365
+ silent: true,
1366
+ silentOnError: true,
1367
+ });
1248
1368
  throw error;
1249
1369
  }
1250
1370
  if (shouldHotReload)
@@ -1257,18 +1377,16 @@ EOF
1257
1377
  // ready replica that made canHotReload true. Do not let the caller delete
1258
1378
  // the old route kind until the replacement edge is actually Available.
1259
1379
  if (!canHotReload || changesNode)
1260
- shellExec(
1261
- `kubectl rollout status deployment/${UNDERPOST_INGRESS.name} -n ${namespace} --timeout=5m`,
1262
- { silent: true },
1263
- );
1380
+ shellExec(`kubectl rollout status deployment/${UNDERPOST_INGRESS.name} -n ${namespace} --timeout=5m`, {
1381
+ silent: true,
1382
+ });
1264
1383
  else if (shouldHotReload)
1265
1384
  // `nginx -s reload` returns after signalling the master. Give it one
1266
1385
  // scheduling turn to start the new workers before the caller removes
1267
1386
  // the old stack's route object.
1268
1387
  shellExec('sleep 1', { silent: true });
1269
1388
  shellExec(
1270
- `kubectl wait --for=condition=Available deployment/${UNDERPOST_INGRESS.name} ` +
1271
- `-n ${namespace} --timeout=5m`,
1389
+ `kubectl wait --for=condition=Available deployment/${UNDERPOST_INGRESS.name} ` + `-n ${namespace} --timeout=5m`,
1272
1390
  { silent: true },
1273
1391
  );
1274
1392
  execIngress('nginx -t -c /tmp/nginx.conf', { silent: true });
@@ -1350,26 +1468,27 @@ EOF
1350
1468
  config(options = { underpostRoot: '.' }) {
1351
1469
  const { underpostRoot } = options;
1352
1470
  console.log('Applying host configuration: SELinux, Docker, Containerd, and Sysctl settings.');
1353
- // Disable SELinux (permissive mode)
1354
- shellExec(`sudo setenforce 0`, {
1355
- silentOnError: true,
1356
- });
1357
- shellExec(`sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config`, {
1358
- silentOnError: true,
1359
- });
1471
+ // Label before enforcing, never after: flipping the mode first would put the host into
1472
+ // Enforcing with the storage trees still carrying the policy default, and every pod that
1473
+ // mounts one would start failing for real instead of only logging a denial.
1474
+ //
1475
+ // Registered on every run, not only at first cluster creation: the node directories backing
1476
+ // PersistentVolumes are recreated by resets, by `DirectoryOrCreate`, and by the local-path
1477
+ // helper pod long after `kubeadm init` ran, and each of those inherits the policy default
1478
+ // unless the mapping is already in the store.
1479
+ ensureSELinuxTooling();
1480
+ Underpost.cluster.ensureContainerStorageContexts();
1481
+ enforceSELinux(['/var/lib/kubelet', '/var/lib/containerd']);
1360
1482
 
1361
1483
  // Enable and start Docker and Kubelet services
1362
1484
  shellExec(`sudo systemctl enable --now docker`); // Docker might not be needed for K3s
1363
1485
  shellExec(`sudo systemctl enable --now kubelet`); // Kubelet might not be needed for K3s (K3s uses its own agent)
1364
1486
 
1365
- // Configure containerd for SystemdCgroup and explicitly disable SELinux
1366
- // This is crucial for kubelet/k3s to interact correctly with containerd
1487
+ // Configure containerd for systemd cgroups and SELinux labeling.
1367
1488
  shellExec(`containerd config default | sudo tee /etc/containerd/config.toml > /dev/null`);
1368
1489
  shellExec(`sudo sed -i -e "s/SystemdCgroup = false/SystemdCgroup = true/g" /etc/containerd/config.toml`);
1369
- // Add a new line to disable SELinux for the runc runtime
1370
- // shellExec(
1371
- // `sudo sed -i '/SystemdCgroup = true/a selinux_disabled = true' /etc/containerd/config.toml`,
1372
- // );
1490
+ shellExec(`sudo sed -i -e "s/enable_selinux = false/enable_selinux = true/g" /etc/containerd/config.toml`);
1491
+ runSELinuxCommands([selinuxRestoreconCommandFactory('/etc/containerd')], { execute: shellExec });
1373
1492
  // Restart docker after containerd config changes. Rocky 9 uses systemctl,
1374
1493
  // not the legacy service command.
1375
1494
  shellExec(`sudo systemctl restart docker || sudo service docker restart || true`);
@@ -1400,7 +1519,7 @@ EOF
1400
1519
  * This applies only what K3s genuinely requires, and every step is guarded
1401
1520
  * so it is a no-op when the relevant tooling is absent (e.g. minimal images
1402
1521
  * without SELinux userspace):
1403
- * - SELinux permissive (only if SELinux tooling is present).
1522
+ * - SELinux remains Enforcing.
1404
1523
  * - swap off (Kubernetes best practice).
1405
1524
  * - br_netfilter + bridge/forward sysctls (pod networking).
1406
1525
  * - inotify limits.
@@ -1415,16 +1534,11 @@ EOF
1415
1534
  // forever (the upstream unit ships TimeoutStartSec=0).
1416
1535
  shellExec(`if systemctl is-active --quiet firewalld; then sudo systemctl disable --now firewalld; fi`);
1417
1536
 
1418
- // SELinux permissive, but only when the tooling exists. Rocky has it;
1419
- // minimal LXD images may not. K3s also installs k3s-selinux for enforcing
1420
- // mode, so this is a best-effort dev convenience, not a hard requirement.
1421
- shellExec(`if command -v setenforce >/dev/null 2>&1; then sudo setenforce 0; fi`, {
1422
- silentOnError: true,
1423
- });
1424
- shellExec(
1425
- `if [ -f /etc/selinux/config ]; then sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config; fi`,
1426
- { silentOnError: true },
1427
- );
1537
+ // Storage labeling precedes the Enforcing flip, so the mode is never activated over trees
1538
+ // that still carry the policy default.
1539
+ ensureSELinuxTooling();
1540
+ Underpost.cluster.ensureContainerStorageContexts();
1541
+ enforceSELinux(K3S_SELINUX_PATHS);
1428
1542
 
1429
1543
  // Disable swap. `swapoff -a` is a no-op without swap; the sed only edits
1430
1544
  // fstab when a swap line is present.
@@ -1697,17 +1811,12 @@ fi`);
1697
1811
  if (options.removeVolumeHostPaths) Underpost.cluster._cleanHostPathPvs();
1698
1812
  else logger.info(' -> Skipping (pass --remove-volume-host-paths to enable).');
1699
1813
 
1700
- logger.info('Phase 2/7: SELinux permissive + restore contexts (when present)...');
1701
- shellExec(`if command -v setenforce >/dev/null 2>&1; then sudo setenforce 0; fi`);
1702
- shellExec(
1703
- `if [ -f /etc/selinux/config ]; then sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config; fi`,
1704
- );
1705
- shellExec(
1706
- `if command -v restorecon >/dev/null 2>&1 && [ -d /var/lib/kubelet ]; then sudo restorecon -Rv /var/lib/kubelet; fi`,
1707
- );
1708
- shellExec(
1709
- `if command -v restorecon >/dev/null 2>&1 && [ -d /var/lib/containerd ]; then sudo restorecon -Rv /var/lib/containerd; fi`,
1710
- );
1814
+ logger.info('Phase 2/7: Enforcing SELinux and restoring runtime contexts...');
1815
+ // The hostPath cleanup above emptied the volume trees; re-asserting the mapping here means
1816
+ // a rebuild starts from correctly labeled storage instead of inheriting the policy default.
1817
+ ensureSELinuxTooling();
1818
+ Underpost.cluster.ensureContainerStorageContexts();
1819
+ enforceSELinux(['/var/lib/kubelet', '/var/lib/containerd']);
1711
1820
 
1712
1821
  logger.info('Phase 3/7: Stopping host kubelet and container runtimes (kubeadm-scope only)...');
1713
1822
  shellExec(`if systemctl is-active --quiet kubelet; then sudo systemctl stop kubelet; fi`);
@@ -1909,7 +2018,7 @@ EOF`);
1909
2018
 
1910
2019
  // SOPS and Age for Git-native encrypted secret manifests. Owned by UnderpostSecret so the
1911
2020
  // same idempotent install backs `underpost secret --install-tools`.
1912
- Underpost.secret.sops.installTooling();
2021
+ Underpost.secret.installTooling();
1913
2022
 
1914
2023
  // Install snap
1915
2024
  shellExec(`sudo yum install -y snapd`);
@@ -1981,10 +2090,8 @@ EOF`);
1981
2090
  shellExec(`sudo rm -f /etc/sysctl.d/99-k8s-ipforward.conf`);
1982
2091
  shellExec(`sudo rm -f /etc/sysctl.d/99-k8s.conf`);
1983
2092
 
1984
- // Restore SELinux to enforcing
1985
- console.log('Restoring SELinux to enforcing mode...');
1986
- // shellExec(`sudo setenforce 1`);
1987
- // shellExec(`sudo sed -i 's/^SELINUX=permissive$/SELINUX=enforcing/' /etc/selinux/config`);
2093
+ console.log('Keeping SELinux in enforcing mode...');
2094
+ enforceSELinux();
1988
2095
 
1989
2096
  console.log('Uninstall process completed.');
1990
2097
  },