@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
@@ -0,0 +1,219 @@
1
+ 'use strict';
2
+
3
+ import { expect } from 'chai';
4
+ import {
5
+ activeExecutionProfile,
6
+ CAPABILITIES,
7
+ classifyCommand,
8
+ cli,
9
+ EXECUTION_PROFILE_ENV_KEY,
10
+ EXECUTION_PROFILES,
11
+ executionDecisionFactory,
12
+ executionProfileFactory,
13
+ profileFromOptionsFactory,
14
+ withExecutionProfile,
15
+ } from '../../src/server/build/execution.js';
16
+
17
+ describe('command capability classification', () => {
18
+ it('separates cluster reads from cluster writes', () => {
19
+ expect(classifyCommand('kubectl get pods')).to.equal('cluster:read');
20
+ expect(classifyCommand('kubectl describe svc x -n default')).to.equal('cluster:read');
21
+ expect(classifyCommand('kubectl apply -f manifest.yaml')).to.equal('cluster:write');
22
+ expect(classifyCommand('kubectl delete secret underpost-config -n default')).to.equal('cluster:write');
23
+ expect(classifyCommand('kubectl exec pod -- sh -c ls')).to.equal('cluster:write');
24
+ });
25
+
26
+ it('finds the verb past kubectl flags that consume their next token', () => {
27
+ expect(classifyCommand('kubectl -n default get pods')).to.equal('cluster:read');
28
+ expect(classifyCommand('kubectl --namespace default --context x get svc')).to.equal('cluster:read');
29
+ expect(classifyCommand('kubectl -n default apply -f x.yaml')).to.equal('cluster:write');
30
+ });
31
+
32
+ it('treats an unrecognised kubectl verb as a write rather than assuming it is harmless', () => {
33
+ expect(classifyCommand('kubectl some-future-verb thing')).to.equal('cluster:write');
34
+ });
35
+
36
+ it('classifies through sudo rather than stopping at it', () => {
37
+ expect(classifyCommand('sudo kubectl apply -f x.yaml')).to.equal('cluster:write');
38
+ expect(classifyCommand('sudo kubectl get pods')).to.equal('cluster:read');
39
+ expect(classifyCommand('sudo -u dd kubectl get pods')).to.equal('cluster:read');
40
+ });
41
+
42
+ it('treats an escalated unknown command as a host mutation', () => {
43
+ expect(classifyCommand('sudo some-vendor-tool --install')).to.equal('host:write');
44
+ expect(classifyCommand('some-vendor-tool --install')).to.equal('fs');
45
+ });
46
+
47
+ it('takes the most privileged segment of a compound command', () => {
48
+ expect(classifyCommand('kubectl create secret generic x --dry-run=client -o yaml | kubectl apply -f -')).to.equal(
49
+ 'cluster:write',
50
+ );
51
+ expect(classifyCommand('cd /home/dd && git add . && kubectl apply -f x.yaml')).to.equal('cluster:write');
52
+ expect(classifyCommand('cd /home/dd/cyberia-instances && git status')).to.equal('git');
53
+ });
54
+
55
+ it('reads a heredoc as data, not as a pipeline', () => {
56
+ const cmd = ["kubectl apply -f - -n default <<'EOF'", 'apiVersion: v1', 'kind: Secret', 'EOF'].join('\n');
57
+ expect(classifyCommand(cmd)).to.equal('cluster:write');
58
+ });
59
+
60
+ it('classifies host, network and git work distinctly', () => {
61
+ expect(classifyCommand('sudo dnf install -y cockpit')).to.equal('host:write');
62
+ expect(classifyCommand('sudo systemctl enable --now cockpit.socket')).to.equal('host:write');
63
+ expect(classifyCommand('curl https://example.com')).to.equal('net');
64
+ expect(classifyCommand('git add .')).to.equal('git');
65
+ });
66
+
67
+ it('lets nested engine invocations through so a profile can reach its own stages', () => {
68
+ expect(classifyCommand('node bin deploy --build-manifest dd development')).to.equal('fs');
69
+ expect(classifyCommand('cd /x && underpost pull . underpostnet/cyberia-instances')).to.equal('fs');
70
+ });
71
+
72
+ it('never returns a capability outside the declared set', () => {
73
+ const commands = [
74
+ '',
75
+ ' ',
76
+ 'echo hi',
77
+ 'NODE_ENV=production node bin client',
78
+ 'sudo',
79
+ 'kubectl',
80
+ 'helm list -n default',
81
+ 'helm upgrade --install x ./chart',
82
+ ];
83
+ for (const cmd of commands) expect(CAPABILITIES).to.include(classifyCommand(cmd));
84
+ });
85
+ });
86
+
87
+ describe('execution profiles', () => {
88
+ it('permits everything under LIVE_CLUSTER', () => {
89
+ for (const capability of CAPABILITIES) expect(EXECUTION_PROFILES.LIVE_CLUSTER.permits).to.include(capability);
90
+ });
91
+
92
+ it('permits build outputs but no environment mutation under HERMETIC_BUILD', () => {
93
+ const { permits } = EXECUTION_PROFILES.HERMETIC_BUILD;
94
+ expect(permits).to.have.members(['fs', 'git']);
95
+ for (const denied of ['cluster:read', 'cluster:write', 'host:write', 'net']) expect(permits).to.not.include(denied);
96
+ });
97
+
98
+ it('permits nothing under OFFLINE_DRY_RUN', () => {
99
+ expect(EXECUTION_PROFILES.OFFLINE_DRY_RUN.permits).to.deep.equal([]);
100
+ });
101
+
102
+ it('falls back to the default profile rather than throwing on an unknown name', () => {
103
+ expect(executionProfileFactory('nope').name).to.equal('LIVE_CLUSTER');
104
+ expect(executionProfileFactory('').name).to.equal('LIVE_CLUSTER');
105
+ expect(executionProfileFactory(undefined).name).to.equal('LIVE_CLUSTER');
106
+ });
107
+
108
+ it('accepts a profile name in either kebab or snake form', () => {
109
+ expect(executionProfileFactory('hermetic-build').name).to.equal('HERMETIC_BUILD');
110
+ expect(executionProfileFactory('hermetic_build').name).to.equal('HERMETIC_BUILD');
111
+ });
112
+ });
113
+
114
+ describe('execution decisions', () => {
115
+ it('denies cluster work under HERMETIC_BUILD and permits build output', () => {
116
+ const denied = executionDecisionFactory('kubectl delete secret x -n default', { profile: 'HERMETIC_BUILD' });
117
+ expect(denied.permitted).to.equal(false);
118
+ expect(denied.capability).to.equal('cluster:write');
119
+ expect(denied.neutral).to.equal(true);
120
+ expect(executionDecisionFactory('git add .', { profile: 'HERMETIC_BUILD' }).permitted).to.equal(true);
121
+ });
122
+
123
+ it('permits the same command under LIVE_CLUSTER', () => {
124
+ expect(
125
+ executionDecisionFactory('kubectl delete secret x -n default', { profile: 'LIVE_CLUSTER' }).permitted,
126
+ ).to.equal(true);
127
+ });
128
+
129
+ it('denies even local work under OFFLINE_DRY_RUN', () => {
130
+ expect(executionDecisionFactory('echo hi', { profile: 'OFFLINE_DRY_RUN' }).permitted).to.equal(false);
131
+ });
132
+ });
133
+
134
+ describe('profile propagation', () => {
135
+ const originalProfile = process.env[EXECUTION_PROFILE_ENV_KEY];
136
+
137
+ afterEach(() => {
138
+ if (originalProfile === undefined) delete process.env[EXECUTION_PROFILE_ENV_KEY];
139
+ else process.env[EXECUTION_PROFILE_ENV_KEY] = originalProfile;
140
+ });
141
+
142
+ it('exports the profile to the environment so child processes inherit it', () => {
143
+ withExecutionProfile('HERMETIC_BUILD', () => {
144
+ expect(process.env[EXECUTION_PROFILE_ENV_KEY]).to.equal('HERMETIC_BUILD');
145
+ expect(activeExecutionProfile().name).to.equal('HERMETIC_BUILD');
146
+ });
147
+ expect(process.env[EXECUTION_PROFILE_ENV_KEY]).to.equal(originalProfile);
148
+ });
149
+
150
+ it('restores the previous profile when the body throws', () => {
151
+ expect(() =>
152
+ withExecutionProfile('OFFLINE_DRY_RUN', () => {
153
+ throw new Error('stage failed');
154
+ }),
155
+ ).to.throw('stage failed');
156
+ expect(process.env[EXECUTION_PROFILE_ENV_KEY]).to.equal(originalProfile);
157
+ });
158
+
159
+ it('restores the previous profile after an async body settles', async () => {
160
+ await withExecutionProfile('HERMETIC_BUILD', async () => {
161
+ expect(activeExecutionProfile().name).to.equal('HERMETIC_BUILD');
162
+ });
163
+ expect(process.env[EXECUTION_PROFILE_ENV_KEY]).to.equal(originalProfile);
164
+ });
165
+
166
+ it('nests without leaking the inner profile outward', () => {
167
+ withExecutionProfile('HERMETIC_BUILD', () => {
168
+ withExecutionProfile('OFFLINE_DRY_RUN', () => {
169
+ expect(activeExecutionProfile().name).to.equal('OFFLINE_DRY_RUN');
170
+ });
171
+ expect(activeExecutionProfile().name).to.equal('HERMETIC_BUILD');
172
+ });
173
+ });
174
+
175
+ it('defaults to LIVE_CLUSTER when the environment says nothing', () => {
176
+ delete process.env[EXECUTION_PROFILE_ENV_KEY];
177
+ expect(activeExecutionProfile().name).to.equal('LIVE_CLUSTER');
178
+ });
179
+ });
180
+
181
+ describe('legacy bypass flag aliases', () => {
182
+ it('prefers an explicit --profile over every alias', () => {
183
+ expect(profileFromOptionsFactory({ profile: 'OFFLINE_DRY_RUN', disableUpdateUnderpostConfig: true })).to.equal(
184
+ 'OFFLINE_DRY_RUN',
185
+ );
186
+ });
187
+
188
+ it('aliases the legacy flag only in the mode where it means the same thing', () => {
189
+ expect(profileFromOptionsFactory({ disableUpdateUnderpostConfig: true, buildManifest: true })).to.equal(
190
+ 'HERMETIC_BUILD',
191
+ );
192
+ // A live deploy carrying the same flag must stay live: it is a step toggle there, and
193
+ // silently eliding every cluster write would turn the deploy into a no-op.
194
+ expect(profileFromOptionsFactory({ disableUpdateUnderpostConfig: true })).to.equal('');
195
+ });
196
+
197
+ it('selects nothing when no flag and no profile is given', () => {
198
+ expect(profileFromOptionsFactory({})).to.equal('');
199
+ expect(profileFromOptionsFactory()).to.equal('');
200
+ });
201
+ });
202
+
203
+ describe('CLI resolution', () => {
204
+ it('resolves underpost to a runnable command', () => {
205
+ const resolved = cli();
206
+ expect(resolved === 'underpost' || /^node \/.+\/bin\/index\.js$/.test(resolved)).to.equal(true);
207
+ });
208
+
209
+ it('re-enters this checkout under local, never a global install', () => {
210
+ // A globally installed underpost is a different package: resolving to it for a stage
211
+ // that operates on this working tree runs the wrong code against the right cwd.
212
+ expect(cli('underpost', { local: true })).to.match(/^node \/.+\/bin\/index\.js$/);
213
+ expect(cli('underpost', { local: true })).to.not.equal('underpost');
214
+ });
215
+
216
+ it('passes through any other binary name unchanged', () => {
217
+ expect(cli('kubectl')).to.equal('kubectl');
218
+ });
219
+ });
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ import { expect } from 'chai';
4
+ import Underpost from '../../src/index.js';
5
+ import { hostAddressesFactory } from '../../src/cli/wireguard.js';
6
+
7
+ const syncPrivateConfMock = vi.hoisted(() => vi.fn());
8
+ vi.mock('../../src/server/runtime/conf.js', async (importOriginal) => ({
9
+ ...(await importOriginal()),
10
+ syncPrivateConf: syncPrivateConfMock,
11
+ }));
12
+
13
+ describe('a fleet command cannot fall back to local execution', () => {
14
+ it('refuses when a remote target resolved no SSH identity', async () => {
15
+ let thrown;
16
+ try {
17
+ await Underpost.event.runCommand('echo should-not-run', { requireRemote: true, host: '10.0.0.9' });
18
+ } catch (error) {
19
+ thrown = error;
20
+ }
21
+ expect(thrown, 'runCommand should have thrown').to.be.an('error');
22
+ expect(thrown.message).to.include('refusing to run locally');
23
+ });
24
+
25
+ it('still runs locally for remediation, which has no such requirement', async () => {
26
+ const result = await Underpost.event.runCommand('echo local-remediation-ok', { silent: true });
27
+ expect(result.ok).to.equal(true);
28
+ expect(`${result.output}`).to.include('local-remediation-ok');
29
+ });
30
+
31
+ it('runs locally from where it is, when the deploy path is not on this machine', async () => {
32
+ // A CI container checks the engine out under /__w/<repo>, so entering the deploy path is
33
+ // impossible there; that chdir failing reported a perfectly runnable command as a failed
34
+ // remediation.
35
+ const output = await Underpost.ssh.sshRemoteRunner('echo local-remediation-ok', {
36
+ cd: '/underpost/absent-deploy-path',
37
+ remote: false,
38
+ silent: true,
39
+ });
40
+ expect(`${output}`).to.include('local-remediation-ok');
41
+ });
42
+ });
43
+
44
+ describe('wireguard sync never switches the checkout it runs from', () => {
45
+ const originalTargets = Underpost.wireguard.syncTargets;
46
+ const originalRunCommand = Underpost.event.runCommand;
47
+ const originalGetDefaultBranch = Underpost.repo.getDefaultBranch;
48
+
49
+ afterEach(() => {
50
+ Underpost.wireguard.syncTargets = originalTargets;
51
+ Underpost.event.runCommand = originalRunCommand;
52
+ Underpost.repo.getDefaultBranch = originalGetDefaultBranch;
53
+ syncPrivateConfMock.mockReset();
54
+ });
55
+
56
+ const stub = (targets) => {
57
+ const dispatched = [];
58
+ Underpost.wireguard.syncTargets = () => targets;
59
+ Underpost.event.runCommand = async (command, options) => {
60
+ dispatched.push({ host: options.host, requireRemote: options.requireRemote });
61
+ return { ok: true, output: '' };
62
+ };
63
+ return dispatched;
64
+ };
65
+
66
+ // Dispatch fixtures use RFC 5737 documentation addresses on purpose: the guard skips any node
67
+ // registered at an address this machine holds, so a fixture that names a real fleet address —
68
+ // the control plane's LAN address, or its 10.0.0.x tunnel address — stops being a remote node
69
+ // on the very hosts the suite runs on.
70
+ const remote = { nodeName: 'vultr', user: 'root', host: '203.0.113.10', via: 'root@203.0.113.10:22' };
71
+
72
+ it('skips a node registered at an address this machine holds', async () => {
73
+ // The peer list is keyed by managementHost because it is unique; a hostname is not, and
74
+ // the control plane and this workstation are both `localhost.localdomain`.
75
+ const [ownAddress] = [...hostAddressesFactory()];
76
+ expect(ownAddress, 'this host must have a routable address to exercise the guard').to.be.a('string');
77
+ const self = { nodeName: 'control', user: 'admin', host: ownAddress, via: 'ssh' };
78
+ const dispatched = stub([self, remote]);
79
+
80
+ await Underpost.wireguard.sync({ cmd: 'echo fleet' });
81
+
82
+ expect(dispatched.map(({ host }) => host)).to.deep.equal(['203.0.113.10']);
83
+ });
84
+
85
+ it('skips a target that resolved to local execution', async () => {
86
+ const local = { nodeName: 'control', user: '', host: '', via: 'local' };
87
+ const dispatched = stub([local, remote]);
88
+
89
+ await Underpost.wireguard.sync({ cmd: 'echo fleet' });
90
+
91
+ expect(dispatched).to.have.lengthOf(1);
92
+ expect(dispatched[0].host).to.equal('203.0.113.10');
93
+ });
94
+
95
+ it('marks every dispatch remote-only', async () => {
96
+ const dispatched = stub([remote]);
97
+ await Underpost.wireguard.sync({ cmd: 'echo fleet' });
98
+ expect(dispatched.every(({ requireRemote }) => requireRemote === true)).to.equal(true);
99
+ });
100
+
101
+ it('fails instead of reporting success when every node is this machine', async () => {
102
+ stub([{ nodeName: 'control', user: '', host: '', via: 'local' }]);
103
+ let thrown;
104
+ try {
105
+ await Underpost.wireguard.sync({ cmd: 'echo fleet' });
106
+ } catch (error) {
107
+ thrown = error;
108
+ }
109
+ expect(thrown, 'an all-local selection must not look like a successful sync').to.be.an('error');
110
+ expect(thrown.message).to.include('sync source');
111
+ });
112
+
113
+ it('still syncs a remote node that merely shares this machine hostname', async () => {
114
+ // The reported case: the control plane and this workstation are both
115
+ // `localhost.localdomain`, so the remote one must still be reached, not skipped.
116
+ const sameName = { nodeName: 'localhost.localdomain', user: 'admin', host: '198.51.100.85', via: 'ssh' };
117
+ const dispatched = stub([sameName]);
118
+ await Underpost.wireguard.sync({ cmd: 'echo fleet' });
119
+ expect(dispatched.map(({ host }) => host)).to.deep.equal(['198.51.100.85']);
120
+ });
121
+
122
+ it('publishes an associated deploy private repo before updating remote nodes', async () => {
123
+ const order = [];
124
+ Underpost.repo.getDefaultBranch = () => 'main';
125
+ Underpost.wireguard.syncTargets = () => [remote];
126
+ syncPrivateConfMock.mockImplementation((deployId) => order.push(`publish:${deployId}`));
127
+ Underpost.event.runCommand = async () => {
128
+ order.push('update');
129
+ return { ok: true, output: '' };
130
+ };
131
+
132
+ await Underpost.wireguard.sync({ repoEngine: 'fixture-org/engine-test-cyberia' });
133
+
134
+ expect(order).to.deep.equal(['publish:dd-cyberia', 'update']);
135
+ });
136
+
137
+ it('does not publish a deploy private repo for the base engine, a custom command, or a dry run', async () => {
138
+ const published = [];
139
+ Underpost.repo.getDefaultBranch = () => 'main';
140
+ syncPrivateConfMock.mockImplementation((deployId) => published.push(deployId));
141
+ stub([remote]);
142
+
143
+ await Underpost.wireguard.sync({ repoEngine: 'fixture-org/engine' });
144
+ await Underpost.wireguard.sync({ repoEngine: 'fixture-org/engine-cyberia', cmd: 'echo fleet' });
145
+ await Underpost.wireguard.sync({ repoEngine: 'fixture-org/engine-cyberia', dryRun: true });
146
+
147
+ expect(published).to.deep.equal([]);
148
+ });
149
+ });
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ import { expect } from 'chai';
4
+ import { PassThrough } from 'node:stream';
5
+ import winston from 'winston';
6
+ import { loggerFormatFactory, redactSensitiveText, serializeLogValue } from '../../src/server/ops/logger.js';
7
+
8
+ const renderLog = (message, metadata) => {
9
+ const stream = new PassThrough();
10
+ let output = '';
11
+ stream.on('data', (chunk) => {
12
+ output += chunk;
13
+ });
14
+ const logger = winston.createLogger({
15
+ format: loggerFormatFactory('logger-redaction.test.js'),
16
+ transports: [new winston.transports.Stream({ stream })],
17
+ });
18
+ metadata === undefined ? logger.info(message) : logger.info(message, metadata);
19
+ logger.close();
20
+ return output;
21
+ };
22
+
23
+ describe('central logger redaction', () => {
24
+ it('filters common secret forms from free-form messages', () => {
25
+ const rawToken = `ghp_${'A'.repeat(30)}`;
26
+ const input = [
27
+ 'node bin host set GITHUB_TOKEN positional-secret',
28
+ 'GITHUB_TOKEN=environment-secret node bin db',
29
+ 'DB_PASS=database-secret',
30
+ 'curl --api-key flag-secret',
31
+ 'Authorization: Bearer bearer-secret',
32
+ 'https://user:url-secret@example.test/repo',
33
+ '?session_id=query-secret&ok=true',
34
+ `raw ${rawToken}`,
35
+ '-----BEGIN PRIVATE KEY-----\nprivate-key-secret\n-----END PRIVATE KEY-----',
36
+ ].join(' && ');
37
+ const output = redactSensitiveText(input);
38
+
39
+ for (const secret of [
40
+ 'positional-secret',
41
+ 'environment-secret',
42
+ 'database-secret',
43
+ 'flag-secret',
44
+ 'bearer-secret',
45
+ 'url-secret',
46
+ 'query-secret',
47
+ 'private-key-secret',
48
+ rawToken,
49
+ ])
50
+ expect(output).to.not.include(secret);
51
+ expect(output).to.include('GITHUB_TOKEN=[REDACTED]');
52
+ expect(output).to.include('https://***@example.test/repo');
53
+ });
54
+
55
+ it('filters nested sensitive fields without mutating the source metadata', () => {
56
+ const metadata = {
57
+ githubToken: 'nested-secret',
58
+ auth: { client_secret: 'client-secret', username: 'operator' },
59
+ command: 'node bin host set API_KEY command-secret',
60
+ };
61
+ metadata.self = metadata;
62
+ const output = serializeLogValue(metadata);
63
+
64
+ expect(output).to.not.include('nested-secret');
65
+ expect(output).to.not.include('client-secret');
66
+ expect(output).to.not.include('command-secret');
67
+ expect(output).to.include('operator');
68
+ expect(output).to.include('[Circular]');
69
+ expect(metadata.githubToken).to.equal('nested-secret');
70
+ });
71
+
72
+ it('applies the filter in the formatter used by every logger transport', () => {
73
+ const output = renderLog('ssh node :: node bin host set GITHUB_TOKEN message-secret', {
74
+ token_secret: 'metadata-secret',
75
+ repository: 'https://user:url-secret@example.test/repo',
76
+ });
77
+
78
+ for (const secret of ['message-secret', 'metadata-secret', 'url-secret']) expect(output).to.not.include(secret);
79
+ expect(output).to.include('[REDACTED]');
80
+ });
81
+
82
+ it('leaves ordinary operational context readable', () => {
83
+ expect(redactSensitiveText('ssh operator@example.test :: uptime')).to.equal('ssh operator@example.test :: uptime');
84
+ });
85
+
86
+ it('filters Git fetch credentials from plain and escaped URLs', () => {
87
+ const token = 'opaque-git-credential_123';
88
+ for (const command of [
89
+ `GIT_TERMINAL_PROMPT=0 git fetch --force "https://x-access-token:${token}@github.com/o/r" main`,
90
+ `GIT_TERMINAL_PROMPT=0 git fetch --force "https:\\/\\/x-access-token:${token}@github.com\\/o\\/r" main`,
91
+ `GIT_TERMINAL_PROMPT=0 git fetch --force "https\\://x-access-token\\:${token}\\@github.com\\/o\\/r" main`,
92
+ `git fetch x-access-token:${token}@github.com`,
93
+ ]) {
94
+ const output = redactSensitiveText(command);
95
+ expect(output).to.not.include(token);
96
+ expect(output).to.match(/\*\*\*|\[REDACTED\]/);
97
+ }
98
+ });
99
+
100
+ it('filters every database password form used by the db workflow', () => {
101
+ for (const command of [
102
+ 'mariadb -u root -psecret database',
103
+ 'mariadb -u root -p "secret" database',
104
+ 'mariadb-dump --user=root --password=secret database',
105
+ 'mongosh -u root -p "secret"',
106
+ 'mongorestore --username root --password "secret"',
107
+ 'MYSQL_PWD=secret mariadb database',
108
+ 'mongodb://root:secret@mongo/database',
109
+ ])
110
+ expect(redactSensitiveText(command), command).to.not.include('secret');
111
+ });
112
+ });