@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
package/src/cli/app.js ADDED
@@ -0,0 +1,391 @@
1
+ /**
2
+ * App domain: one application deployment's runtime environment.
3
+ *
4
+ * Its durable source is that deployment's env file under `engine-private/conf/<deployId>/`.
5
+ * The deployment id is resolved from the repository context, never passed as a path, so a
6
+ * caller says `--env development` and gets `.env.development` — or its sub-configuration
7
+ * variant when `--args sub-conf=<name>` names one.
8
+ *
9
+ * A custom instance is a deployment too: `--args instance-id=<id>` addresses that instance's own
10
+ * `instances/<id>/env/<env>.env` and its own `<deployId>-<instanceId>-<env>-env` Secret, so an
11
+ * instance's runtime environment is managed through this domain rather than by reaching into the
12
+ * private conf tree by hand.
13
+ *
14
+ * Implements the canonical domain action set; see {@link UnderpostDomains.DOMAIN_ACTIONS}.
15
+ * @module src/cli/app.js
16
+ * @namespace UnderpostApp
17
+ */
18
+
19
+ import dotenv from 'dotenv';
20
+ import fs from 'fs-extra';
21
+
22
+ import {
23
+ cleanDeployEnvFiles,
24
+ DEFAULT_DEPLOY_ID,
25
+ deployEnvContentFactory,
26
+ deployEnvFilePath,
27
+ deployOciEnvFilePath,
28
+ instanceEnvFilePath,
29
+ loadConf,
30
+ } from '../server/runtime/conf.js';
31
+ import { domainContextFactory } from './domains.js';
32
+ import { loggerFactory } from '../server/ops/logger.js';
33
+ import { readDeployRoutes } from '../server/network/router.js';
34
+ import { shellExec } from '../server/runtime/process.js';
35
+ import { writeEnv } from '../server/runtime/environment.js';
36
+ import { isReservedEnvKey } from './host.js';
37
+ import Underpost from '../index.js';
38
+
39
+ const logger = loggerFactory(import.meta);
40
+
41
+ /**
42
+ * Name of the Kubernetes Secret a deployment's environment is projected into. An instance carries
43
+ * its own, under the `<deployId>-<instanceId>` prefix every other instance object already uses.
44
+ */
45
+ const appSecretName = (deployId, env, instanceId = '') =>
46
+ `${instanceId ? `${deployId}-${instanceId}` : deployId}-${env}-env`;
47
+
48
+ // This domain's local runtime: the working-tree file `loadConf` materializes and the server
49
+ // reads. `publish` is the inverse of `load`, so both ends name the same file.
50
+ const LOCAL_RUNTIME_ENV_PATH = './.env';
51
+
52
+ /**
53
+ * @class UnderpostApp
54
+ * @description Application deployment environment domain.
55
+ * @memberof UnderpostApp
56
+ */
57
+ class UnderpostApp {
58
+ static API = {
59
+ /**
60
+ * Resolves the deployment id from the repository/deployment context.
61
+ *
62
+ * Most specific first: an explicit `--args deploy-id=`, the ambient `DEPLOY_ID` a loaded
63
+ * environment exports, the host store the node was bootstrapped with, then the first routed
64
+ * deploy. Falls back to the default deploy id, so resolution is total.
65
+ * @param {object} [context] - Normalized domain context, or `{ args }`.
66
+ * @returns {string} Deployment id.
67
+ * @memberof UnderpostApp
68
+ */
69
+ deployId(context = {}) {
70
+ const routed = readDeployRoutes();
71
+ return (
72
+ `${context.args?.['deploy-id'] ?? context.deployId ?? ''}`.trim() ||
73
+ `${process.env.DEPLOY_ID || ''}`.trim() ||
74
+ `${Underpost.host.store.get('DEPLOY_ID', undefined, { disableLog: true }) || ''}`.trim() ||
75
+ (routed.length > 0 ? routed[0] : '') ||
76
+ DEFAULT_DEPLOY_ID
77
+ );
78
+ },
79
+
80
+ /**
81
+ * Resolves the custom instance this invocation addresses, if any.
82
+ * @param {object} [context] - Normalized domain context, or `{ args }`.
83
+ * @returns {string} Instance id, or the empty string for the deployment itself.
84
+ * @memberof UnderpostApp
85
+ */
86
+ instanceId(context = {}) {
87
+ return `${context.args?.['instance-id'] ?? context.instanceId ?? ''}`.trim();
88
+ },
89
+
90
+ /**
91
+ * Resolves the durable source for an environment.
92
+ *
93
+ * `--args instance-id=` selects that instance's own env file. Otherwise a named
94
+ * sub-configuration selects `.env.<env>.<subConf>` when that file exists and degrades
95
+ * to the plain `.env.<env>` when it does not — the same precedence
96
+ * {@link ServerConfBuilder.loadConf} applies, so both agree on which file is in effect.
97
+ * @param {object} [context] - Normalized domain context.
98
+ * @returns {string} Path to the deployment or instance env file.
99
+ * @memberof UnderpostApp
100
+ */
101
+ envPath(context = {}) {
102
+ const env = `${context.env ?? ''}`.trim() || 'production';
103
+ const instanceId = UnderpostApp.API.instanceId(context);
104
+ if (instanceId) return instanceEnvFilePath(UnderpostApp.API.deployId(context), instanceId, env);
105
+ return deployEnvFilePath(
106
+ UnderpostApp.API.deployId(context),
107
+ env,
108
+ `${context.args?.['sub-conf'] ?? process.env.DEPLOY_SUB_CONF ?? ''}`.trim(),
109
+ );
110
+ },
111
+
112
+ /**
113
+ * Resolves the environment for a container runtime: the durable source with its
114
+ * `.env.<env>.oci` overlay applied. Used wherever the consumer is a container image rather
115
+ * than this host — the cluster Secret projection above all.
116
+ * @param {object} [context] - Normalized domain context.
117
+ * @returns {{source: string, overlay: string|null, content: string, values: Object<string,string>}}
118
+ * The resolved environment.
119
+ * @memberof UnderpostApp
120
+ */
121
+ ociEnv(context = {}) {
122
+ // An instance env file is already the container's environment — `instance-build-manifest`
123
+ // derives it for that runtime — so there is no host/container split to overlay away.
124
+ const source = UnderpostApp.API.envPath(context);
125
+ if (UnderpostApp.API.instanceId(context))
126
+ return {
127
+ source,
128
+ overlay: null,
129
+ content: fs.existsSync(source) ? fs.readFileSync(source, 'utf8') : '',
130
+ values: UnderpostApp.API.read(context),
131
+ };
132
+ return deployEnvContentFactory(
133
+ UnderpostApp.API.deployId(context),
134
+ `${context.env ?? ''}`.trim() || 'production',
135
+ `${context.args?.['sub-conf'] ?? process.env.DEPLOY_SUB_CONF ?? ''}`.trim(),
136
+ { oci: true },
137
+ );
138
+ },
139
+
140
+ /**
141
+ * Parses a deployment's environment. An absent source reads as `{}`.
142
+ * @param {object} [context] - Normalized domain context.
143
+ * @returns {Object<string, string>} Parsed deployment environment.
144
+ * @memberof UnderpostApp
145
+ */
146
+ read(context = {}) {
147
+ const envPath = UnderpostApp.API.envPath(context);
148
+ return fs.existsSync(envPath) ? dotenv.parse(fs.readFileSync(envPath, 'utf8')) : {};
149
+ },
150
+
151
+ // ── canonical domain actions ────────────────────────────────────────────────────────────
152
+
153
+ /**
154
+ * Onboards the app domain: resolves the deployment id, confirms its environment source
155
+ * exists, then loads it. Idempotent.
156
+ * @param {object} context - Normalized domain context.
157
+ * @returns {{deployId: string, source: string, keys: number}} What was onboarded.
158
+ * @memberof UnderpostApp
159
+ */
160
+ setup(context = {}) {
161
+ context = domainContextFactory(context);
162
+ const deployId = UnderpostApp.API.deployId(context);
163
+ const source = UnderpostApp.API.envPath(context);
164
+ if (!fs.existsSync(source)) throw new Error(`[app] deployment environment not found: ${source}`);
165
+ if (context.dryRun) {
166
+ logger.info('[dry-run] app setup would load', { deployId, source });
167
+ return { deployId, source, keys: Object.keys(UnderpostApp.API.read(context)).length };
168
+ }
169
+ const { keys } = UnderpostApp.API.load(context);
170
+ return { deployId, source, keys };
171
+ },
172
+
173
+ /**
174
+ * Materializes the deployment environment into this domain's local runtime: the working-tree
175
+ * `./.env` copies and the in-process `Config.default` the server reads.
176
+ *
177
+ * Deterministic: the same environment selector and repository context always resolve the
178
+ * same file, and the working tree is rebuilt from it rather than merged into.
179
+ * @param {object} context - Normalized domain context.
180
+ * @returns {{source: string, keys: number}} Which file was loaded and how much of it.
181
+ * @memberof UnderpostApp
182
+ */
183
+ load(context = {}) {
184
+ context = domainContextFactory(context);
185
+ const deployId = UnderpostApp.API.deployId(context);
186
+ const envPath = UnderpostApp.API.envPath(context);
187
+ if (!fs.existsSync(envPath)) throw new Error(`[app] deployment environment not found: ${envPath}`);
188
+ const values = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
189
+ if (context.dryRun) {
190
+ logger.info('[dry-run] app load would materialize the working tree', {
191
+ deployId,
192
+ source: envPath,
193
+ keys: Object.keys(values).length,
194
+ });
195
+ return { source: envPath, keys: Object.keys(values).length };
196
+ }
197
+ // Deliberately not the host configuration store: that store is host-scoped and holds the node's
198
+ // own configuration, so writing a deployment's environment there would erase it.
199
+ process.env.NODE_ENV = context.env;
200
+ const instanceId = UnderpostApp.API.instanceId(context);
201
+ // `loadConf` materializes a deployment's working tree from its own conf; an instance has no
202
+ // conf of its own, so its file is the local runtime env directly.
203
+ if (instanceId) writeEnv(LOCAL_RUNTIME_ENV_PATH, values);
204
+ else loadConf(deployId, `${context.args['sub-conf'] ?? ''}`.trim() || undefined);
205
+ // The resolution loadConf performed, not `ociEnv()`, which forces the overlay on: a
206
+ // skipped overlay and an applied one were indistinguishable in the pod log.
207
+ const resolved = deployEnvContentFactory(deployId, context.env, `${context.args?.['sub-conf'] ?? ''}`.trim());
208
+ logger.info('Deployment environment loaded', {
209
+ deployId,
210
+ instanceId: instanceId || null,
211
+ source: envPath,
212
+ inContainer: Underpost.state.isInsideContainer(),
213
+ ociOverlay: resolved.overlay,
214
+ keys: Object.keys(values).length,
215
+ });
216
+ return { source: envPath, keys: Object.keys(values).length };
217
+ },
218
+
219
+ /**
220
+ * Writes this domain's local runtime back into the deployment's durable source.
221
+ *
222
+ * The exact inverse of `load`, so it reads the working-tree `./.env` that `load` materialized
223
+ * — never the host configuration store, which belongs to the host domain and carries the node's
224
+ * configuration rather than this deployment's.
225
+ * Refuses to overwrite an existing source without `--force`.
226
+ * @param {object} context - Normalized domain context.
227
+ * @returns {{target: string, keys: number}} What was written.
228
+ * @memberof UnderpostApp
229
+ */
230
+ publish(context = {}) {
231
+ context = domainContextFactory(context);
232
+ const target = UnderpostApp.API.envPath(context);
233
+ const source = LOCAL_RUNTIME_ENV_PATH;
234
+ if (!fs.existsSync(source))
235
+ throw new Error(`[app] ${source} not found; run \`underpost app load\` before publishing`);
236
+ const values = dotenv.parse(fs.readFileSync(source, 'utf8'));
237
+ const keys = Object.keys(values);
238
+ if (keys.length === 0) throw new Error(`[app] ${source} is empty; nothing to publish`);
239
+ if (fs.existsSync(target) && !context.force)
240
+ throw new Error(`[app] ${target} already exists; re-run with --force to overwrite it`);
241
+ if (context.dryRun) {
242
+ logger.info('[dry-run] app publish would write', { target, keys: keys.length });
243
+ return { target, keys: keys.length };
244
+ }
245
+ writeEnv(target, values);
246
+ logger.info('Deployment environment published', { target, keys: keys.length });
247
+ return { target, keys: keys.length };
248
+ },
249
+
250
+ /**
251
+ * Projects the deployment environment into the cluster as its own Secret, so a workload can
252
+ * inject it with `envFrom` alongside the host configuration.
253
+ * Idempotent — delete-then-apply converges on the current source.
254
+ * @param {object} context - Normalized domain context.
255
+ * @returns {{secret: string, namespace: string, env: string}} What was projected.
256
+ * @memberof UnderpostApp
257
+ */
258
+ apply(context = {}) {
259
+ context = domainContextFactory(context);
260
+ const deployId = UnderpostApp.API.deployId(context);
261
+ const envFilePath = UnderpostApp.API.envPath(context);
262
+ if (!fs.existsSync(envFilePath)) throw new Error(`[app] deployment environment not found: ${envFilePath}`);
263
+ const secret = appSecretName(deployId, context.env, UnderpostApp.API.instanceId(context));
264
+ // This Secret is consumed only by container workloads, so the OCI overlay is applied
265
+ // unconditionally here — the host projecting it is not the runtime that reads it.
266
+ const { overlay, content } = UnderpostApp.API.ociEnv(context);
267
+ if (context.dryRun) {
268
+ logger.info('[dry-run] app apply would publish the Secret', {
269
+ secret,
270
+ namespace: context.namespace,
271
+ source: envFilePath,
272
+ overlay,
273
+ });
274
+ return { secret, namespace: context.namespace, env: context.env, overlay };
275
+ }
276
+ // The same reserved-key filter the host domain applies: an injected PATH would override
277
+ // the container image's own no matter which Secret carried it.
278
+ const sanitizedEnvPath = `${envFilePath}.secret`;
279
+ fs.writeFileSync(
280
+ sanitizedEnvPath,
281
+ content
282
+ .split('\n')
283
+ .filter((line) => {
284
+ const trimmed = line.trimStart();
285
+ if (!trimmed || trimmed.startsWith('#')) return true;
286
+ const key = line.slice(0, line.indexOf('=')).trim();
287
+ return !key || !isReservedEnvKey(key);
288
+ })
289
+ .join('\n'),
290
+ );
291
+ try {
292
+ shellExec(`kubectl delete secret ${secret} -n ${context.namespace} --ignore-not-found`);
293
+ shellExec(
294
+ `kubectl create secret generic ${secret} --from-env-file=${sanitizedEnvPath} --dry-run=client -o yaml | kubectl apply -f - -n ${context.namespace}`,
295
+ );
296
+ } finally {
297
+ fs.removeSync(sanitizedEnvPath);
298
+ }
299
+ logger.info('Deployment environment applied', {
300
+ secret,
301
+ env: context.env,
302
+ namespace: context.namespace,
303
+ overlay,
304
+ });
305
+ return { secret, namespace: context.namespace, env: context.env, overlay };
306
+ },
307
+
308
+ /**
309
+ * Read-only report: the resolved deployment id, which source is in effect, and whether the
310
+ * cluster projection exists.
311
+ * @param {object} context - Normalized domain context.
312
+ * @returns {object} Report.
313
+ * @memberof UnderpostApp
314
+ */
315
+ status(context = {}) {
316
+ context = domainContextFactory(context);
317
+ const deployId = UnderpostApp.API.deployId(context);
318
+ const source = UnderpostApp.API.envPath(context);
319
+ const present = fs.existsSync(source);
320
+ const secret = appSecretName(deployId, context.env, UnderpostApp.API.instanceId(context));
321
+ const projected = shellExec(`kubectl get secret ${secret} -n ${context.namespace} -o name`, {
322
+ silent: true,
323
+ stdout: true,
324
+ silentOnError: true,
325
+ disableLog: true,
326
+ });
327
+ const instanceId = UnderpostApp.API.instanceId(context);
328
+ const ociOverlay = instanceId ? '' : deployOciEnvFilePath(deployId, context.env);
329
+ const report = {
330
+ domain: 'app',
331
+ env: context.env,
332
+ namespace: context.namespace,
333
+ deployId,
334
+ instanceId: instanceId || null,
335
+ source,
336
+ sourcePresent: present,
337
+ ociOverlay: ociOverlay && fs.existsSync(ociOverlay) ? ociOverlay : null,
338
+ keys: present ? Object.keys(UnderpostApp.API.read(context)).length : 0,
339
+ clusterSecret: `${projected ?? ''}`.trim() ? secret : null,
340
+ };
341
+ logger.info('app status', report);
342
+ return report;
343
+ },
344
+
345
+ /**
346
+ * Replaces the live projection: deletes the deployment's Secret and re-applies it.
347
+ * @param {object} context - Normalized domain context.
348
+ * @returns {object} The re-applied projection.
349
+ * @memberof UnderpostApp
350
+ */
351
+ rotate(context = {}) {
352
+ context = domainContextFactory(context);
353
+ const secret = appSecretName(
354
+ UnderpostApp.API.deployId(context),
355
+ context.env,
356
+ UnderpostApp.API.instanceId(context),
357
+ );
358
+ if (context.dryRun) {
359
+ logger.info('[dry-run] app rotate would re-project the Secret', { secret, namespace: context.namespace });
360
+ return { secret, namespace: context.namespace, env: context.env };
361
+ }
362
+ shellExec(`kubectl delete secret ${secret} -n ${context.namespace} --ignore-not-found`);
363
+ return UnderpostApp.API.apply(context);
364
+ },
365
+
366
+ /**
367
+ * Withdraws the deployment environment from the local filesystem: removes the working-tree
368
+ * `.env*` files `loadConf` materializes. `--force` also drops the deployment's cluster Secret.
369
+ * @param {object} context - Normalized domain context.
370
+ * @returns {{removed: Array<string>, removedSecret: string|null}} What was withdrawn.
371
+ * @memberof UnderpostApp
372
+ */
373
+ clean(context = {}) {
374
+ context = domainContextFactory(context);
375
+ const secret = context.force
376
+ ? appSecretName(UnderpostApp.API.deployId(context), context.env, UnderpostApp.API.instanceId(context))
377
+ : null;
378
+ if (context.dryRun) {
379
+ logger.info('[dry-run] app clean would remove the working-tree env files', { secret });
380
+ return { removed: [], removedSecret: secret };
381
+ }
382
+ const removed = cleanDeployEnvFiles();
383
+ if (secret) shellExec(`kubectl delete secret ${secret} -n ${context.namespace} --ignore-not-found`);
384
+ logger.info('Deployment environment withdrawn', { files: removed, secret });
385
+ return { removed, removedSecret: secret };
386
+ },
387
+ };
388
+ }
389
+
390
+ export default UnderpostApp;
391
+ export { appSecretName, UnderpostApp };
@@ -5,13 +5,12 @@
5
5
  */
6
6
 
7
7
  import { fileURLToPath } from 'url';
8
- import { getNpmRootPath, getUnderpostRootPath } from '../server/conf.js';
9
- import { pbcopy, shellExec } from '../server/process.js';
10
- import dotenv from 'dotenv';
11
- import { loggerFactory, loggerMiddleware } from '../server/logger.js';
8
+ import { getNpmRootPath } from '../server/runtime/environment.js';
9
+ import { pbcopy, shellExec } from '../server/runtime/process.js';
10
+ import { loggerFactory, loggerMiddleware } from '../server/ops/logger.js';
12
11
  import fs from 'fs-extra';
13
12
  import path from 'path';
14
- import Downloader from '../server/downloader.js';
13
+ import Downloader from '../server/storage/downloader.js';
15
14
  import { newInstance, range, s4, timer } from '../client/components/core/CommonJs.js';
16
15
  import { spawnSync } from 'child_process';
17
16
  import Underpost from '../index.js';
@@ -98,7 +97,7 @@ class UnderpostBaremetal {
98
97
  * @param {boolean} [options.worker=false] - Flag to designate the machine as a worker node.
99
98
  * @param {string} [options.control=''] - Specifies the control node for the baremetal machine.
100
99
  * @param {string} [options.sshKeyDir=''] - Specifies the directory containing SSH keys for the baremetal machine.
101
- * @param {string} [options.deployId=''] - Specifies the deployment ID for SSH key resolution.
100
+ * @param {string} [options.deployId=''] - Specifies the deployment ID used to resolve the private engine repo cloned onto the node.
102
101
  * @param {string} [options.engineRepo=''] - Specifies the custom engine repository URL.
103
102
  * @param {string} [options.engineBranch=''] - Specifies the custom engine repository branch.
104
103
  * @param {string} [options.enginePrivateRepo=''] - Specifies the custom private engine repository URL.
@@ -944,7 +943,7 @@ rm -rf ${artifacts.join(' ')}`);
944
943
  `id root`,
945
944
  `ls -la /home/root/.ssh/`,
946
945
  `cat /home/root/.ssh/authorized_keys`,
947
- 'underpost test',
946
+ 'cd /home/dd/engine && node bin test unit,infra',
948
947
  ],
949
948
  });
950
949
  }
@@ -1491,7 +1490,7 @@ rm -rf ${artifacts.join(' ')}`);
1491
1490
  if (!joinResult.ok || !joinCommand) {
1492
1491
  throw new Error(
1493
1492
  `Failed to retrieve kubeadm join command from control-plane ${controlUser}@${controlIp} using key "${keyPath}" (code ${joinResult.code}). ` +
1494
- `Ensure that key authorizes ${controlUser}@${controlIp} — pass --deploy-id <id> --user <user> (key from engine-private/conf/<id>/users/<user>) ` +
1493
+ `Ensure that key authorizes ${controlUser}@${controlIp} — pass --user <user> (key from engine-private/deploy/users/<user>) ` +
1495
1494
  `or --ssh-key-dir <dir> to use the key/user the control-plane accepts. stderr: ${joinResult.stderr.slice(-300)}`,
1496
1495
  );
1497
1496
  }
@@ -1515,7 +1514,6 @@ rm -rf ${artifacts.join(' ')}`);
1515
1514
  }
1516
1515
 
1517
1516
  // Secrets needed on the node to clone the private engine-private repo
1518
- // (engine-private/conf/.../.env.production for `node bin run secret`).
1519
1517
  const githubUsername = Underpost.baremetal.readEngineConfig('GITHUB_USERNAME') || 'underpostnet';
1520
1518
  const githubEnv = {
1521
1519
  GITHUB_TOKEN: Underpost.baremetal.readEngineConfig('GITHUB_TOKEN'),
@@ -1989,7 +1987,7 @@ rm -rf ${artifacts.join(' ')}`);
1989
1987
  try {
1990
1988
  return { machine };
1991
1989
  } catch (error) {
1992
- console.log(error);
1990
+ console.error(error);
1993
1991
  logger.error(error);
1994
1992
  throw new Error(`Failed to create MAAS machine. Output:\n${machine}`);
1995
1993
  }
@@ -2598,11 +2596,10 @@ shell
2598
2596
  *
2599
2597
  * Key-dir precedence:
2600
2598
  * 1. `--ssh-key-dir <dir>` (explicit path)
2601
- * 2. `--deploy-id <id>` (+ `--user <user>`) → `engine-private/conf/<id>/users/<user>`
2602
- * (the same user↔deployId↔key convention `src/cli/ssh.js` writes; no key path needed)
2603
- * 3. workflow `sshKeyDir`
2604
- * 4. workflow `deployId` (+ `user`)
2605
- * 5. default `engine-private/deploy`
2599
+ * 2. workflow `sshKeyDir`
2600
+ * 3. a non-root user `engine-private/deploy/users/<user>`
2601
+ * (the cluster scoped key store `src/cli/ssh.js` writes; no key path needed)
2602
+ * 4. default `engine-private/deploy` (the controller key, i.e. root)
2606
2603
  *
2607
2604
  * User precedence: `--user` > workflow `user` > `root`. A leading `~` in the
2608
2605
  * resolved dir is expanded to the user's home.
@@ -2616,13 +2613,13 @@ shell
2616
2613
  resolveSshKeyPaths({ options = {}, workflowsConfig = {}, workflowId = '' } = {}) {
2617
2614
  const wf = workflowsConfig?.[workflowId] || {};
2618
2615
  const user = options.user || wf.user || 'root';
2616
+ // Kept for the private engine repo naming only; SSH keys are cluster scoped.
2619
2617
  const deployId = options.deployId || wf.deployId || '';
2620
2618
 
2621
2619
  let dir;
2622
2620
  if (options.sshKeyDir) dir = options.sshKeyDir;
2623
- else if (options.deployId) dir = `engine-private/conf/${options.deployId}/users/${user}`;
2624
2621
  else if (wf.sshKeyDir) dir = wf.sshKeyDir;
2625
- else if (deployId) dir = `engine-private/conf/${deployId}/users/${user}`;
2622
+ else if (user !== 'root') dir = path.normalize(Underpost.ssh.userKeyDir(user));
2626
2623
  else dir = 'engine-private/deploy';
2627
2624
 
2628
2625
  if (dir.startsWith('~')) dir = path.join(process.env.HOME || '', dir.slice(1));
@@ -2634,10 +2631,10 @@ shell
2634
2631
  * @description Resolves the login accounts baked into the deployed OS. Always
2635
2632
  * creates TWO admin accounts so a console login is guaranteed:
2636
2633
  * 1. the MAAS admin (MAAS_ADMIN_USERNAME / MAAS_ADMIN_PASS) — password login,
2637
- * 2. the deploy user (`--user`, e.g. admin) — its conf.node.json
2638
- * password (or MAAS_ADMIN_PASS), plus the SSH key.
2639
- * root always gets MAAS_ADMIN_PASS. The deploy user/password come from
2640
- * `engine-private/conf/<deployId>/conf.node.json` (`users[user]`).
2634
+ * 2. the deploy user (`--user`, e.g. admin) — its cluster config password
2635
+ * (or MAAS_ADMIN_PASS), plus the SSH key.
2636
+ * root always gets MAAS_ADMIN_PASS. The deploy user/password come from the
2637
+ * cluster users registry `engine-private/deploy/conf.users.json`.
2641
2638
  * @param {object} params
2642
2639
  * @param {object} [params.options={}] - CLI options.
2643
2640
  * @param {object} [params.workflowsConfig={}] - Loaded workflows config.
@@ -2646,21 +2643,18 @@ shell
2646
2643
  * @memberof UnderpostBaremetal
2647
2644
  */
2648
2645
  resolveInstallCredentials({ options = {}, workflowsConfig = {}, workflowId = '' } = {}) {
2649
- const { user, deployId } = Underpost.baremetal.resolveSshKeyPaths({ options, workflowsConfig, workflowId });
2646
+ const { user } = Underpost.baremetal.resolveSshKeyPaths({ options, workflowsConfig, workflowId });
2650
2647
 
2651
2648
  let confUser = {};
2652
- if (deployId) {
2653
- const confPath = `engine-private/conf/${deployId}/conf.node.json`;
2654
- if (fs.existsSync(confPath)) {
2655
- try {
2656
- confUser = JSON.parse(fs.readFileSync(confPath, 'utf8'))?.users?.[user] || {};
2657
- } catch (error) {
2658
- logger.warn(`Failed to parse ${confPath}: ${error.message}`);
2659
- }
2660
- } else {
2661
- logger.warn(`conf.node.json not found at ${confPath}; using MAAS_ADMIN_* defaults`);
2662
- }
2649
+ try {
2650
+ confUser = Underpost.ssh.findUser(user) || {};
2651
+ } catch (error) {
2652
+ logger.warn(`Failed to parse ${Underpost.ssh.usersConfPath()}: ${error.message}`);
2663
2653
  }
2654
+ if (!confUser.user)
2655
+ logger.warn(
2656
+ `User '${user}' is not registered in ${Underpost.ssh.usersConfPath()}; using MAAS_ADMIN_* defaults`,
2657
+ );
2664
2658
 
2665
2659
  const maasPass = process.env.MAAS_ADMIN_PASS || '';
2666
2660
  const rootPassword = maasPass;
@@ -2668,7 +2662,7 @@ shell
2668
2662
  const adminUsername = process.env.MAAS_ADMIN_USERNAME || 'maas';
2669
2663
  const adminPassword = maasPass;
2670
2664
  // User 2: the deploy user (only when distinct from root/maas admin).
2671
- const deployUsername = deployId && user && user !== 'root' && user !== adminUsername ? user : '';
2665
+ const deployUsername = user && user !== 'root' && user !== adminUsername ? user : '';
2672
2666
  const deployPassword = deployUsername ? confUser.password || maasPass : '';
2673
2667
 
2674
2668
  if (!rootPassword) {
@@ -2683,14 +2677,14 @@ shell
2683
2677
  * @method readEngineConfig
2684
2678
  * @description Reads an engine config/secret value (e.g. GITHUB_TOKEN) on the
2685
2679
  * controller. Prefers the loaded process env, falling back to
2686
- * `node bin config get --plain <key>`.
2680
+ * `node bin host get --plain <key>`.
2687
2681
  * @param {string} key - Config/env key name.
2688
2682
  * @returns {string} The resolved value, or '' if unset.
2689
2683
  * @memberof UnderpostBaremetal
2690
2684
  */
2691
2685
  readEngineConfig(key) {
2692
2686
  if (process.env[key]) return process.env[key];
2693
- const out = shellExec(`node bin config get --plain ${key}`, {
2687
+ const out = shellExec(`node bin host get --plain ${key}`, {
2694
2688
  stdout: true,
2695
2689
  silent: true,
2696
2690
  silentOnError: true,