@omnitron-dev/omnitron 0.2.0 → 0.3.0

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 (1247) hide show
  1. package/dist/BUILD.json +5 -0
  2. package/dist/cli/omnitron.js +351 -29
  3. package/dist/cli/omnitron.js.map +1 -1
  4. package/dist/cluster/cluster.rpc-service.d.ts +40 -0
  5. package/dist/cluster/cluster.rpc-service.d.ts.map +1 -1
  6. package/dist/cluster/cluster.rpc-service.js +49 -3
  7. package/dist/cluster/cluster.rpc-service.js.map +1 -1
  8. package/dist/cluster/config-sync.d.ts.map +1 -1
  9. package/dist/cluster/config-sync.js.map +1 -1
  10. package/dist/cluster/leader-election.d.ts +90 -2
  11. package/dist/cluster/leader-election.d.ts.map +1 -1
  12. package/dist/cluster/leader-election.js +176 -22
  13. package/dist/cluster/leader-election.js.map +1 -1
  14. package/dist/cluster/mesh-link.d.ts +136 -0
  15. package/dist/cluster/mesh-link.d.ts.map +1 -0
  16. package/dist/cluster/mesh-link.js +212 -0
  17. package/dist/cluster/mesh-link.js.map +1 -0
  18. package/dist/cluster/mesh.d.ts +59 -0
  19. package/dist/cluster/mesh.d.ts.map +1 -0
  20. package/dist/cluster/mesh.js +117 -0
  21. package/dist/cluster/mesh.js.map +1 -0
  22. package/dist/cluster/slave-connector.d.ts +193 -9
  23. package/dist/cluster/slave-connector.d.ts.map +1 -1
  24. package/dist/cluster/slave-connector.js +409 -37
  25. package/dist/cluster/slave-connector.js.map +1 -1
  26. package/dist/commands/audit.d.ts +65 -0
  27. package/dist/commands/audit.d.ts.map +1 -0
  28. package/dist/commands/audit.js +233 -0
  29. package/dist/commands/audit.js.map +1 -0
  30. package/dist/commands/backup.d.ts +4 -0
  31. package/dist/commands/backup.d.ts.map +1 -1
  32. package/dist/commands/backup.js +255 -33
  33. package/dist/commands/backup.js.map +1 -1
  34. package/dist/commands/cluster.d.ts.map +1 -1
  35. package/dist/commands/cluster.js +78 -47
  36. package/dist/commands/cluster.js.map +1 -1
  37. package/dist/commands/daemon-cmd.d.ts +7 -1
  38. package/dist/commands/daemon-cmd.d.ts.map +1 -1
  39. package/dist/commands/daemon-cmd.js +87 -21
  40. package/dist/commands/daemon-cmd.js.map +1 -1
  41. package/dist/commands/daemon-required.d.ts +34 -0
  42. package/dist/commands/daemon-required.d.ts.map +1 -0
  43. package/dist/commands/daemon-required.js +92 -0
  44. package/dist/commands/daemon-required.js.map +1 -0
  45. package/dist/commands/deploy.d.ts +70 -8
  46. package/dist/commands/deploy.d.ts.map +1 -1
  47. package/dist/commands/deploy.js +99 -64
  48. package/dist/commands/deploy.js.map +1 -1
  49. package/dist/commands/discover.d.ts +25 -4
  50. package/dist/commands/discover.d.ts.map +1 -1
  51. package/dist/commands/discover.js +155 -102
  52. package/dist/commands/discover.js.map +1 -1
  53. package/dist/commands/doctor.d.ts +293 -0
  54. package/dist/commands/doctor.d.ts.map +1 -0
  55. package/dist/commands/doctor.js +1706 -0
  56. package/dist/commands/doctor.js.map +1 -0
  57. package/dist/commands/env.d.ts +8 -1
  58. package/dist/commands/env.d.ts.map +1 -1
  59. package/dist/commands/env.js +13 -8
  60. package/dist/commands/env.js.map +1 -1
  61. package/dist/commands/exec.d.ts.map +1 -1
  62. package/dist/commands/exec.js +3 -2
  63. package/dist/commands/exec.js.map +1 -1
  64. package/dist/commands/fleet-asking.d.ts +87 -0
  65. package/dist/commands/fleet-asking.d.ts.map +1 -0
  66. package/dist/commands/fleet-asking.js +176 -0
  67. package/dist/commands/fleet-asking.js.map +1 -0
  68. package/dist/commands/fleet.d.ts +18 -0
  69. package/dist/commands/fleet.d.ts.map +1 -1
  70. package/dist/commands/fleet.js +314 -74
  71. package/dist/commands/fleet.js.map +1 -1
  72. package/dist/commands/health-check.d.ts +8 -6
  73. package/dist/commands/health-check.d.ts.map +1 -1
  74. package/dist/commands/health-check.js +26 -23
  75. package/dist/commands/health-check.js.map +1 -1
  76. package/dist/commands/health.d.ts.map +1 -1
  77. package/dist/commands/health.js +22 -8
  78. package/dist/commands/health.js.map +1 -1
  79. package/dist/commands/infra.d.ts +42 -1
  80. package/dist/commands/infra.d.ts.map +1 -1
  81. package/dist/commands/infra.js +418 -49
  82. package/dist/commands/infra.js.map +1 -1
  83. package/dist/commands/init.d.ts.map +1 -1
  84. package/dist/commands/init.js +21 -36
  85. package/dist/commands/init.js.map +1 -1
  86. package/dist/commands/inspect-graph.d.ts.map +1 -1
  87. package/dist/commands/inspect-graph.js +2 -6
  88. package/dist/commands/inspect-graph.js.map +1 -1
  89. package/dist/commands/inspect.d.ts.map +1 -1
  90. package/dist/commands/inspect.js +110 -14
  91. package/dist/commands/inspect.js.map +1 -1
  92. package/dist/commands/k8s.d.ts.map +1 -1
  93. package/dist/commands/k8s.js +23 -18
  94. package/dist/commands/k8s.js.map +1 -1
  95. package/dist/commands/kb.d.ts.map +1 -1
  96. package/dist/commands/kb.js +88 -29
  97. package/dist/commands/kb.js.map +1 -1
  98. package/dist/commands/list.d.ts.map +1 -1
  99. package/dist/commands/list.js +21 -9
  100. package/dist/commands/list.js.map +1 -1
  101. package/dist/commands/log-health.d.ts +120 -0
  102. package/dist/commands/log-health.d.ts.map +1 -0
  103. package/dist/commands/log-health.js +212 -0
  104. package/dist/commands/log-health.js.map +1 -0
  105. package/dist/commands/logs.d.ts +96 -2
  106. package/dist/commands/logs.d.ts.map +1 -1
  107. package/dist/commands/logs.js +522 -196
  108. package/dist/commands/logs.js.map +1 -1
  109. package/dist/commands/metrics.d.ts.map +1 -1
  110. package/dist/commands/metrics.js +35 -17
  111. package/dist/commands/metrics.js.map +1 -1
  112. package/dist/commands/monit.d.ts.map +1 -1
  113. package/dist/commands/monit.js +3 -2
  114. package/dist/commands/monit.js.map +1 -1
  115. package/dist/commands/node.d.ts +68 -0
  116. package/dist/commands/node.d.ts.map +1 -1
  117. package/dist/commands/node.js +285 -68
  118. package/dist/commands/node.js.map +1 -1
  119. package/dist/commands/output.d.ts +10 -0
  120. package/dist/commands/output.d.ts.map +1 -1
  121. package/dist/commands/output.js +41 -1
  122. package/dist/commands/output.js.map +1 -1
  123. package/dist/commands/pipeline.d.ts.map +1 -1
  124. package/dist/commands/pipeline.js +26 -19
  125. package/dist/commands/pipeline.js.map +1 -1
  126. package/dist/commands/project.d.ts +19 -0
  127. package/dist/commands/project.d.ts.map +1 -1
  128. package/dist/commands/project.js +125 -27
  129. package/dist/commands/project.js.map +1 -1
  130. package/dist/commands/release-store.d.ts +28 -0
  131. package/dist/commands/release-store.d.ts.map +1 -0
  132. package/dist/commands/release-store.js +158 -0
  133. package/dist/commands/release-store.js.map +1 -0
  134. package/dist/commands/release.d.ts +61 -0
  135. package/dist/commands/release.d.ts.map +1 -0
  136. package/dist/commands/release.js +555 -0
  137. package/dist/commands/release.js.map +1 -0
  138. package/dist/commands/reload.d.ts +16 -0
  139. package/dist/commands/reload.d.ts.map +1 -1
  140. package/dist/commands/reload.js +21 -5
  141. package/dist/commands/reload.js.map +1 -1
  142. package/dist/commands/remote.d.ts +40 -0
  143. package/dist/commands/remote.d.ts.map +1 -1
  144. package/dist/commands/remote.js +146 -35
  145. package/dist/commands/remote.js.map +1 -1
  146. package/dist/commands/restart.d.ts +16 -0
  147. package/dist/commands/restart.d.ts.map +1 -1
  148. package/dist/commands/restart.js +21 -5
  149. package/dist/commands/restart.js.map +1 -1
  150. package/dist/commands/scale.d.ts.map +1 -1
  151. package/dist/commands/scale.js +4 -3
  152. package/dist/commands/scale.js.map +1 -1
  153. package/dist/commands/secret.d.ts +54 -0
  154. package/dist/commands/secret.d.ts.map +1 -1
  155. package/dist/commands/secret.js +236 -34
  156. package/dist/commands/secret.js.map +1 -1
  157. package/dist/commands/service-units.d.ts +181 -0
  158. package/dist/commands/service-units.d.ts.map +1 -0
  159. package/dist/commands/service-units.js +291 -0
  160. package/dist/commands/service-units.js.map +1 -0
  161. package/dist/commands/service.d.ts +39 -0
  162. package/dist/commands/service.d.ts.map +1 -0
  163. package/dist/commands/service.js +370 -0
  164. package/dist/commands/service.js.map +1 -0
  165. package/dist/commands/spinner.d.ts +29 -0
  166. package/dist/commands/spinner.d.ts.map +1 -0
  167. package/dist/commands/spinner.js +44 -0
  168. package/dist/commands/spinner.js.map +1 -0
  169. package/dist/commands/stack.d.ts +49 -1
  170. package/dist/commands/stack.d.ts.map +1 -1
  171. package/dist/commands/stack.js +325 -12
  172. package/dist/commands/stack.js.map +1 -1
  173. package/dist/commands/start.d.ts +16 -0
  174. package/dist/commands/start.d.ts.map +1 -1
  175. package/dist/commands/start.js +20 -3
  176. package/dist/commands/start.js.map +1 -1
  177. package/dist/commands/status.d.ts +9 -0
  178. package/dist/commands/status.d.ts.map +1 -1
  179. package/dist/commands/status.js +45 -29
  180. package/dist/commands/status.js.map +1 -1
  181. package/dist/commands/stop.d.ts +16 -0
  182. package/dist/commands/stop.d.ts.map +1 -1
  183. package/dist/commands/stop.js +34 -6
  184. package/dist/commands/stop.js.map +1 -1
  185. package/dist/commands/tor.d.ts +14 -7
  186. package/dist/commands/tor.d.ts.map +1 -1
  187. package/dist/commands/tor.js +113 -33
  188. package/dist/commands/tor.js.map +1 -1
  189. package/dist/commands/up.d.ts +85 -1
  190. package/dist/commands/up.d.ts.map +1 -1
  191. package/dist/commands/up.js +180 -32
  192. package/dist/commands/up.js.map +1 -1
  193. package/dist/commands/webapp.d.ts.map +1 -1
  194. package/dist/commands/webapp.js +2 -1
  195. package/dist/commands/webapp.js.map +1 -1
  196. package/dist/config/defaults.d.ts +16 -0
  197. package/dist/config/defaults.d.ts.map +1 -1
  198. package/dist/config/defaults.js +26 -3
  199. package/dist/config/defaults.js.map +1 -1
  200. package/dist/config/define-ecosystem.d.ts.map +1 -1
  201. package/dist/config/define-ecosystem.js +2 -0
  202. package/dist/config/define-ecosystem.js.map +1 -1
  203. package/dist/config/index.d.ts +1 -1
  204. package/dist/config/index.d.ts.map +1 -1
  205. package/dist/config/index.js +1 -1
  206. package/dist/config/index.js.map +1 -1
  207. package/dist/config/loader.d.ts +45 -1
  208. package/dist/config/loader.d.ts.map +1 -1
  209. package/dist/config/loader.js +64 -6
  210. package/dist/config/loader.js.map +1 -1
  211. package/dist/config/omnitron-config.schema.d.ts.map +1 -1
  212. package/dist/config/omnitron-config.schema.js +16 -0
  213. package/dist/config/omnitron-config.schema.js.map +1 -1
  214. package/dist/config/types.d.ts +411 -42
  215. package/dist/config/types.d.ts.map +1 -1
  216. package/dist/daemon/config-reload.d.ts +27 -0
  217. package/dist/daemon/config-reload.d.ts.map +1 -0
  218. package/dist/daemon/config-reload.js +42 -0
  219. package/dist/daemon/config-reload.js.map +1 -0
  220. package/dist/daemon/daemon-client.d.ts +136 -2
  221. package/dist/daemon/daemon-client.d.ts.map +1 -1
  222. package/dist/daemon/daemon-client.js +307 -21
  223. package/dist/daemon/daemon-client.js.map +1 -1
  224. package/dist/daemon/daemon-entry.js +90 -30
  225. package/dist/daemon/daemon-entry.js.map +1 -1
  226. package/dist/daemon/daemon-scheduler.d.ts +4 -0
  227. package/dist/daemon/daemon-scheduler.d.ts.map +1 -1
  228. package/dist/daemon/daemon-scheduler.js +137 -7
  229. package/dist/daemon/daemon-scheduler.js.map +1 -1
  230. package/dist/daemon/daemon-state-store.service.d.ts +263 -0
  231. package/dist/daemon/daemon-state-store.service.d.ts.map +1 -0
  232. package/dist/daemon/daemon-state-store.service.js +446 -0
  233. package/dist/daemon/daemon-state-store.service.js.map +1 -0
  234. package/dist/daemon/daemon.d.ts +205 -1
  235. package/dist/daemon/daemon.d.ts.map +1 -1
  236. package/dist/daemon/daemon.js +1139 -139
  237. package/dist/daemon/daemon.js.map +1 -1
  238. package/dist/daemon/daemon.module.d.ts.map +1 -1
  239. package/dist/daemon/daemon.module.js +163 -83
  240. package/dist/daemon/daemon.module.js.map +1 -1
  241. package/dist/daemon/daemon.rpc-service.d.ts +90 -1
  242. package/dist/daemon/daemon.rpc-service.d.ts.map +1 -1
  243. package/dist/daemon/daemon.rpc-service.js +438 -96
  244. package/dist/daemon/daemon.rpc-service.js.map +1 -1
  245. package/dist/daemon/pid-manager.d.ts +16 -3
  246. package/dist/daemon/pid-manager.d.ts.map +1 -1
  247. package/dist/daemon/pid-manager.js +31 -4
  248. package/dist/daemon/pid-manager.js.map +1 -1
  249. package/dist/daemon/spawn-daemon.d.ts +73 -0
  250. package/dist/daemon/spawn-daemon.d.ts.map +1 -0
  251. package/dist/daemon/spawn-daemon.js +101 -0
  252. package/dist/daemon/spawn-daemon.js.map +1 -0
  253. package/dist/daemon/state-store.d.ts +65 -3
  254. package/dist/daemon/state-store.d.ts.map +1 -1
  255. package/dist/daemon/state-store.js +100 -58
  256. package/dist/daemon/state-store.js.map +1 -1
  257. package/dist/database/connection.d.ts +71 -0
  258. package/dist/database/connection.d.ts.map +1 -0
  259. package/dist/database/connection.js +129 -0
  260. package/dist/database/connection.js.map +1 -0
  261. package/dist/database/dialect.d.ts +60 -0
  262. package/dist/database/dialect.d.ts.map +1 -0
  263. package/dist/database/dialect.js +80 -0
  264. package/dist/database/dialect.js.map +1 -0
  265. package/dist/database/migrate.d.ts +5 -2
  266. package/dist/database/migrate.d.ts.map +1 -1
  267. package/dist/database/migrate.js +23 -33
  268. package/dist/database/migrate.js.map +1 -1
  269. package/dist/database/migration-runner.d.ts +50 -0
  270. package/dist/database/migration-runner.d.ts.map +1 -0
  271. package/dist/database/migration-runner.js +143 -0
  272. package/dist/database/migration-runner.js.map +1 -0
  273. package/dist/database/migrations/006_login_throttle.d.ts +17 -0
  274. package/dist/database/migrations/006_login_throttle.d.ts.map +1 -0
  275. package/dist/database/migrations/006_login_throttle.js +33 -0
  276. package/dist/database/migrations/006_login_throttle.js.map +1 -0
  277. package/dist/database/migrations/007_sync_ingested.d.ts +21 -0
  278. package/dist/database/migrations/007_sync_ingested.d.ts.map +1 -0
  279. package/dist/database/migrations/007_sync_ingested.js +39 -0
  280. package/dist/database/migrations/007_sync_ingested.js.map +1 -0
  281. package/dist/database/migrations/008_node_health_version_text.d.ts +24 -0
  282. package/dist/database/migrations/008_node_health_version_text.d.ts.map +1 -0
  283. package/dist/database/migrations/008_node_health_version_text.js +35 -0
  284. package/dist/database/migrations/008_node_health_version_text.js.map +1 -0
  285. package/dist/database/migrations/009_node_health_unmeasured.d.ts +40 -0
  286. package/dist/database/migrations/009_node_health_unmeasured.d.ts.map +1 -0
  287. package/dist/database/migrations/009_node_health_unmeasured.js +63 -0
  288. package/dist/database/migrations/009_node_health_unmeasured.js.map +1 -0
  289. package/dist/database/migrations/010_metrics_raw_had_no_reader.d.ts +22 -0
  290. package/dist/database/migrations/010_metrics_raw_had_no_reader.d.ts.map +1 -0
  291. package/dist/database/migrations/010_metrics_raw_had_no_reader.js +26 -0
  292. package/dist/database/migrations/010_metrics_raw_had_no_reader.js.map +1 -0
  293. package/dist/database/migrations/011_alerts_nobody_had_to_write.d.ts +56 -0
  294. package/dist/database/migrations/011_alerts_nobody_had_to_write.d.ts.map +1 -0
  295. package/dist/database/migrations/011_alerts_nobody_had_to_write.js +79 -0
  296. package/dist/database/migrations/011_alerts_nobody_had_to_write.js.map +1 -0
  297. package/dist/database/migrations/012_a_disk_nobody_watched.d.ts +26 -0
  298. package/dist/database/migrations/012_a_disk_nobody_watched.d.ts.map +1 -0
  299. package/dist/database/migrations/012_a_disk_nobody_watched.js +40 -0
  300. package/dist/database/migrations/012_a_disk_nobody_watched.js.map +1 -0
  301. package/dist/database/migrations/013_an_alarm_delivered.d.ts +14 -0
  302. package/dist/database/migrations/013_an_alarm_delivered.d.ts.map +1 -0
  303. package/dist/database/migrations/013_an_alarm_delivered.js +28 -0
  304. package/dist/database/migrations/013_an_alarm_delivered.js.map +1 -0
  305. package/dist/database/migrations/index.d.ts +21 -0
  306. package/dist/database/migrations/index.d.ts.map +1 -0
  307. package/dist/database/migrations/index.js +47 -0
  308. package/dist/database/migrations/index.js.map +1 -0
  309. package/dist/database/schema.d.ts +27 -11
  310. package/dist/database/schema.d.ts.map +1 -1
  311. package/dist/database/sqlite-date-binding.d.ts +71 -0
  312. package/dist/database/sqlite-date-binding.d.ts.map +1 -0
  313. package/dist/database/sqlite-date-binding.js +174 -0
  314. package/dist/database/sqlite-date-binding.js.map +1 -0
  315. package/dist/execution/data-channel.d.ts +48 -0
  316. package/dist/execution/data-channel.d.ts.map +1 -0
  317. package/dist/execution/data-channel.js +55 -0
  318. package/dist/execution/data-channel.js.map +1 -0
  319. package/dist/execution/execution.service.d.ts +139 -0
  320. package/dist/execution/execution.service.d.ts.map +1 -1
  321. package/dist/execution/execution.service.js +273 -10
  322. package/dist/execution/execution.service.js.map +1 -1
  323. package/dist/index.d.ts +1 -1
  324. package/dist/index.d.ts.map +1 -1
  325. package/dist/infrastructure/bare-metal-plan.d.ts +208 -0
  326. package/dist/infrastructure/bare-metal-plan.d.ts.map +1 -0
  327. package/dist/infrastructure/bare-metal-plan.js +363 -0
  328. package/dist/infrastructure/bare-metal-plan.js.map +1 -0
  329. package/dist/infrastructure/bare-metal-runner.d.ts +65 -0
  330. package/dist/infrastructure/bare-metal-runner.d.ts.map +1 -0
  331. package/dist/infrastructure/bare-metal-runner.js +354 -0
  332. package/dist/infrastructure/bare-metal-runner.js.map +1 -0
  333. package/dist/infrastructure/config-payload.d.ts +149 -0
  334. package/dist/infrastructure/config-payload.d.ts.map +1 -0
  335. package/dist/infrastructure/config-payload.js +235 -0
  336. package/dist/infrastructure/config-payload.js.map +1 -0
  337. package/dist/infrastructure/container-runtime.d.ts +169 -1
  338. package/dist/infrastructure/container-runtime.d.ts.map +1 -1
  339. package/dist/infrastructure/container-runtime.js +525 -33
  340. package/dist/infrastructure/container-runtime.js.map +1 -1
  341. package/dist/infrastructure/gateway-reachability.d.ts +85 -0
  342. package/dist/infrastructure/gateway-reachability.d.ts.map +1 -0
  343. package/dist/infrastructure/gateway-reachability.js +116 -0
  344. package/dist/infrastructure/gateway-reachability.js.map +1 -0
  345. package/dist/infrastructure/gateway-redis-db.d.ts +29 -0
  346. package/dist/infrastructure/gateway-redis-db.d.ts.map +1 -0
  347. package/dist/infrastructure/gateway-redis-db.js +32 -0
  348. package/dist/infrastructure/gateway-redis-db.js.map +1 -0
  349. package/dist/infrastructure/host-inspection.d.ts +223 -0
  350. package/dist/infrastructure/host-inspection.d.ts.map +1 -0
  351. package/dist/infrastructure/host-inspection.js +501 -0
  352. package/dist/infrastructure/host-inspection.js.map +1 -0
  353. package/dist/infrastructure/infrastructure-gate.d.ts.map +1 -1
  354. package/dist/infrastructure/infrastructure.service.d.ts +206 -6
  355. package/dist/infrastructure/infrastructure.service.d.ts.map +1 -1
  356. package/dist/infrastructure/infrastructure.service.js +415 -89
  357. package/dist/infrastructure/infrastructure.service.js.map +1 -1
  358. package/dist/infrastructure/known-machines.d.ts +76 -0
  359. package/dist/infrastructure/known-machines.d.ts.map +1 -0
  360. package/dist/infrastructure/known-machines.js +94 -0
  361. package/dist/infrastructure/known-machines.js.map +1 -0
  362. package/dist/infrastructure/node-credentials.d.ts +45 -0
  363. package/dist/infrastructure/node-credentials.d.ts.map +1 -0
  364. package/dist/infrastructure/node-credentials.js +80 -0
  365. package/dist/infrastructure/node-credentials.js.map +1 -0
  366. package/dist/infrastructure/orphan-containers.d.ts +99 -0
  367. package/dist/infrastructure/orphan-containers.d.ts.map +1 -0
  368. package/dist/infrastructure/orphan-containers.js +106 -0
  369. package/dist/infrastructure/orphan-containers.js.map +1 -0
  370. package/dist/infrastructure/phantom-endpoint-janitor.d.ts +20 -0
  371. package/dist/infrastructure/phantom-endpoint-janitor.d.ts.map +1 -1
  372. package/dist/infrastructure/phantom-endpoint-janitor.js +56 -6
  373. package/dist/infrastructure/phantom-endpoint-janitor.js.map +1 -1
  374. package/dist/infrastructure/presets/gateway-openresty.d.ts.map +1 -1
  375. package/dist/infrastructure/presets/gateway-openresty.js +29 -3
  376. package/dist/infrastructure/presets/gateway-openresty.js.map +1 -1
  377. package/dist/infrastructure/presets/minio.d.ts.map +1 -1
  378. package/dist/infrastructure/presets/minio.js +36 -3
  379. package/dist/infrastructure/presets/minio.js.map +1 -1
  380. package/dist/infrastructure/presets/postgres.d.ts.map +1 -1
  381. package/dist/infrastructure/presets/postgres.js +69 -2
  382. package/dist/infrastructure/presets/postgres.js.map +1 -1
  383. package/dist/infrastructure/presets/redis.d.ts.map +1 -1
  384. package/dist/infrastructure/presets/redis.js +13 -0
  385. package/dist/infrastructure/presets/redis.js.map +1 -1
  386. package/dist/infrastructure/presets/registry.d.ts.map +1 -1
  387. package/dist/infrastructure/presets/registry.js +81 -12
  388. package/dist/infrastructure/presets/registry.js.map +1 -1
  389. package/dist/infrastructure/presets/tor.d.ts +38 -0
  390. package/dist/infrastructure/presets/tor.d.ts.map +1 -1
  391. package/dist/infrastructure/presets/tor.js +78 -69
  392. package/dist/infrastructure/presets/tor.js.map +1 -1
  393. package/dist/infrastructure/presets/torrc.d.ts +54 -0
  394. package/dist/infrastructure/presets/torrc.d.ts.map +1 -0
  395. package/dist/infrastructure/presets/torrc.js +153 -0
  396. package/dist/infrastructure/presets/torrc.js.map +1 -0
  397. package/dist/infrastructure/presets/types.d.ts +17 -0
  398. package/dist/infrastructure/presets/types.d.ts.map +1 -1
  399. package/dist/infrastructure/provisioning-outcome.d.ts +85 -0
  400. package/dist/infrastructure/provisioning-outcome.d.ts.map +1 -0
  401. package/dist/infrastructure/provisioning-outcome.js +122 -0
  402. package/dist/infrastructure/provisioning-outcome.js.map +1 -0
  403. package/dist/infrastructure/server-registry.d.ts +27 -0
  404. package/dist/infrastructure/server-registry.d.ts.map +1 -1
  405. package/dist/infrastructure/server-registry.js +60 -5
  406. package/dist/infrastructure/server-registry.js.map +1 -1
  407. package/dist/infrastructure/service-binding.d.ts +60 -0
  408. package/dist/infrastructure/service-binding.d.ts.map +1 -0
  409. package/dist/infrastructure/service-binding.js +90 -0
  410. package/dist/infrastructure/service-binding.js.map +1 -0
  411. package/dist/infrastructure/service-credentials.d.ts +105 -0
  412. package/dist/infrastructure/service-credentials.d.ts.map +1 -0
  413. package/dist/infrastructure/service-credentials.js +141 -0
  414. package/dist/infrastructure/service-credentials.js.map +1 -0
  415. package/dist/infrastructure/service-resolver.d.ts +160 -12
  416. package/dist/infrastructure/service-resolver.d.ts.map +1 -1
  417. package/dist/infrastructure/service-resolver.js +463 -53
  418. package/dist/infrastructure/service-resolver.js.map +1 -1
  419. package/dist/infrastructure/shipped-config.d.ts +43 -0
  420. package/dist/infrastructure/shipped-config.d.ts.map +1 -0
  421. package/dist/infrastructure/shipped-config.js +159 -0
  422. package/dist/infrastructure/shipped-config.js.map +1 -0
  423. package/dist/infrastructure/stack-infra-manager.d.ts +6 -0
  424. package/dist/infrastructure/stack-infra-manager.d.ts.map +1 -1
  425. package/dist/infrastructure/stack-infra-manager.js +31 -13
  426. package/dist/infrastructure/stack-infra-manager.js.map +1 -1
  427. package/dist/infrastructure/types.d.ts +261 -6
  428. package/dist/infrastructure/types.d.ts.map +1 -1
  429. package/dist/mcp/mcp-bridge.d.ts.map +1 -1
  430. package/dist/mcp/mcp-bridge.js.map +1 -1
  431. package/dist/mcp/tool-groups/apps.tools.d.ts +2 -1
  432. package/dist/mcp/tool-groups/apps.tools.d.ts.map +1 -1
  433. package/dist/mcp/tool-groups/apps.tools.js +2 -2
  434. package/dist/mcp/tool-groups/apps.tools.js.map +1 -1
  435. package/dist/mcp/tool-groups/infra.tools.d.ts +16 -2
  436. package/dist/mcp/tool-groups/infra.tools.d.ts.map +1 -1
  437. package/dist/mcp/tool-groups/infra.tools.js +51 -41
  438. package/dist/mcp/tool-groups/infra.tools.js.map +1 -1
  439. package/dist/mcp/tool-groups/kb.tools.d.ts.map +1 -1
  440. package/dist/mcp/tool-groups/kb.tools.js +4 -6
  441. package/dist/mcp/tool-groups/kb.tools.js.map +1 -1
  442. package/dist/mcp/tool-groups/management.tools.d.ts +20 -3
  443. package/dist/mcp/tool-groups/management.tools.d.ts.map +1 -1
  444. package/dist/mcp/tool-groups/management.tools.js +158 -125
  445. package/dist/mcp/tool-groups/management.tools.js.map +1 -1
  446. package/dist/mcp/tool-groups/monitoring.tools.d.ts +10 -1
  447. package/dist/mcp/tool-groups/monitoring.tools.d.ts.map +1 -1
  448. package/dist/mcp/tool-groups/monitoring.tools.js +65 -18
  449. package/dist/mcp/tool-groups/monitoring.tools.js.map +1 -1
  450. package/dist/mcp/unavailable-tools.d.ts +35 -0
  451. package/dist/mcp/unavailable-tools.d.ts.map +1 -0
  452. package/dist/mcp/unavailable-tools.js +59 -0
  453. package/dist/mcp/unavailable-tools.js.map +1 -0
  454. package/dist/monitoring/app-health.indicator.d.ts.map +1 -1
  455. package/dist/monitoring/app-health.indicator.js +12 -0
  456. package/dist/monitoring/app-health.indicator.js.map +1 -1
  457. package/dist/monitoring/desktop-notifier.d.ts +26 -0
  458. package/dist/monitoring/desktop-notifier.d.ts.map +1 -0
  459. package/dist/monitoring/desktop-notifier.js +49 -0
  460. package/dist/monitoring/desktop-notifier.js.map +1 -0
  461. package/dist/monitoring/docker-health.indicator.d.ts +17 -1
  462. package/dist/monitoring/docker-health.indicator.d.ts.map +1 -1
  463. package/dist/monitoring/docker-health.indicator.js +38 -8
  464. package/dist/monitoring/docker-health.indicator.js.map +1 -1
  465. package/dist/monitoring/event-loop-stall.indicator.d.ts +26 -0
  466. package/dist/monitoring/event-loop-stall.indicator.d.ts.map +1 -0
  467. package/dist/monitoring/event-loop-stall.indicator.js +47 -0
  468. package/dist/monitoring/event-loop-stall.indicator.js.map +1 -0
  469. package/dist/monitoring/event-loop-watch.d.ts +124 -0
  470. package/dist/monitoring/event-loop-watch.d.ts.map +1 -0
  471. package/dist/monitoring/event-loop-watch.js +179 -0
  472. package/dist/monitoring/event-loop-watch.js.map +1 -0
  473. package/dist/monitoring/host-disk.d.ts +6 -0
  474. package/dist/monitoring/host-disk.d.ts.map +1 -0
  475. package/dist/monitoring/host-disk.js +26 -0
  476. package/dist/monitoring/host-disk.js.map +1 -0
  477. package/dist/monitoring/log-line.d.ts +188 -0
  478. package/dist/monitoring/log-line.d.ts.map +1 -0
  479. package/dist/monitoring/log-line.js +458 -0
  480. package/dist/monitoring/log-line.js.map +1 -0
  481. package/dist/monitoring/log-manager.d.ts +89 -8
  482. package/dist/monitoring/log-manager.d.ts.map +1 -1
  483. package/dist/monitoring/log-manager.js +246 -77
  484. package/dist/monitoring/log-manager.js.map +1 -1
  485. package/dist/observability/metrics-bridge.d.ts.map +1 -1
  486. package/dist/observability/metrics-server.d.ts.map +1 -1
  487. package/dist/orchestrator/app-handle.d.ts +157 -2
  488. package/dist/orchestrator/app-handle.d.ts.map +1 -1
  489. package/dist/orchestrator/app-handle.js +165 -3
  490. package/dist/orchestrator/app-handle.js.map +1 -1
  491. package/dist/orchestrator/app-health.d.ts +33 -0
  492. package/dist/orchestrator/app-health.d.ts.map +1 -0
  493. package/dist/orchestrator/app-health.js +74 -0
  494. package/dist/orchestrator/app-health.js.map +1 -0
  495. package/dist/orchestrator/app-traffic.d.ts +14 -0
  496. package/dist/orchestrator/app-traffic.d.ts.map +1 -0
  497. package/dist/orchestrator/app-traffic.js +33 -0
  498. package/dist/orchestrator/app-traffic.js.map +1 -0
  499. package/dist/orchestrator/bootstrap-loader-child.d.ts +19 -0
  500. package/dist/orchestrator/bootstrap-loader-child.d.ts.map +1 -0
  501. package/dist/orchestrator/bootstrap-loader-child.js +43 -0
  502. package/dist/orchestrator/bootstrap-loader-child.js.map +1 -0
  503. package/dist/orchestrator/bootstrap-loader.d.ts +72 -0
  504. package/dist/orchestrator/bootstrap-loader.d.ts.map +1 -1
  505. package/dist/orchestrator/bootstrap-loader.js +350 -5
  506. package/dist/orchestrator/bootstrap-loader.js.map +1 -1
  507. package/dist/orchestrator/bootstrap-process.d.ts +99 -4
  508. package/dist/orchestrator/bootstrap-process.d.ts.map +1 -1
  509. package/dist/orchestrator/bootstrap-process.js +330 -19
  510. package/dist/orchestrator/bootstrap-process.js.map +1 -1
  511. package/dist/orchestrator/build-service.d.ts.map +1 -1
  512. package/dist/orchestrator/build-service.js.map +1 -1
  513. package/dist/orchestrator/call-exposed-service.d.ts +7 -0
  514. package/dist/orchestrator/call-exposed-service.d.ts.map +1 -0
  515. package/dist/orchestrator/call-exposed-service.js +50 -0
  516. package/dist/orchestrator/call-exposed-service.js.map +1 -0
  517. package/dist/orchestrator/classic-launcher.d.ts +1 -0
  518. package/dist/orchestrator/classic-launcher.d.ts.map +1 -1
  519. package/dist/orchestrator/classic-launcher.js +34 -3
  520. package/dist/orchestrator/classic-launcher.js.map +1 -1
  521. package/dist/orchestrator/file-watcher.d.ts +78 -1
  522. package/dist/orchestrator/file-watcher.d.ts.map +1 -1
  523. package/dist/orchestrator/file-watcher.js +181 -6
  524. package/dist/orchestrator/file-watcher.js.map +1 -1
  525. package/dist/orchestrator/module-worker-process.d.ts.map +1 -1
  526. package/dist/orchestrator/module-worker-process.js +10 -0
  527. package/dist/orchestrator/module-worker-process.js.map +1 -1
  528. package/dist/orchestrator/orchestrator.service.d.ts +371 -11
  529. package/dist/orchestrator/orchestrator.service.d.ts.map +1 -1
  530. package/dist/orchestrator/orchestrator.service.js +1602 -269
  531. package/dist/orchestrator/orchestrator.service.js.map +1 -1
  532. package/dist/orchestrator/owned-pids.d.ts +83 -0
  533. package/dist/orchestrator/owned-pids.d.ts.map +1 -0
  534. package/dist/orchestrator/owned-pids.js +93 -0
  535. package/dist/orchestrator/owned-pids.js.map +1 -0
  536. package/dist/orchestrator/port-conflict.d.ts +102 -0
  537. package/dist/orchestrator/port-conflict.d.ts.map +1 -0
  538. package/dist/orchestrator/port-conflict.js +131 -0
  539. package/dist/orchestrator/port-conflict.js.map +1 -0
  540. package/dist/orchestrator/process-janitor.d.ts +78 -4
  541. package/dist/orchestrator/process-janitor.d.ts.map +1 -1
  542. package/dist/orchestrator/process-janitor.js +246 -30
  543. package/dist/orchestrator/process-janitor.js.map +1 -1
  544. package/dist/orchestrator/ps-metrics.d.ts +37 -0
  545. package/dist/orchestrator/ps-metrics.d.ts.map +1 -0
  546. package/dist/orchestrator/ps-metrics.js +60 -0
  547. package/dist/orchestrator/ps-metrics.js.map +1 -0
  548. package/dist/orchestrator/service-router.d.ts +58 -2
  549. package/dist/orchestrator/service-router.d.ts.map +1 -1
  550. package/dist/orchestrator/service-router.js +157 -25
  551. package/dist/orchestrator/service-router.js.map +1 -1
  552. package/dist/orchestrator/topology-query.d.ts +49 -0
  553. package/dist/orchestrator/topology-query.d.ts.map +1 -0
  554. package/dist/orchestrator/topology-query.js +54 -0
  555. package/dist/orchestrator/topology-query.js.map +1 -0
  556. package/dist/project/artifact-builder.d.ts +174 -3
  557. package/dist/project/artifact-builder.d.ts.map +1 -1
  558. package/dist/project/artifact-builder.js +454 -42
  559. package/dist/project/artifact-builder.js.map +1 -1
  560. package/dist/project/build-decision.d.ts +58 -0
  561. package/dist/project/build-decision.d.ts.map +1 -0
  562. package/dist/project/build-decision.js +59 -0
  563. package/dist/project/build-decision.js.map +1 -0
  564. package/dist/project/config-resolver.d.ts +6 -4
  565. package/dist/project/config-resolver.d.ts.map +1 -1
  566. package/dist/project/config-resolver.js +128 -74
  567. package/dist/project/config-resolver.js.map +1 -1
  568. package/dist/project/declared-config.d.ts +42 -0
  569. package/dist/project/declared-config.d.ts.map +1 -0
  570. package/dist/project/declared-config.js +60 -0
  571. package/dist/project/declared-config.js.map +1 -0
  572. package/dist/project/dependency-analyzer.d.ts.map +1 -1
  573. package/dist/project/dependency-analyzer.js.map +1 -1
  574. package/dist/project/deploy-phases.d.ts +35 -0
  575. package/dist/project/deploy-phases.d.ts.map +1 -0
  576. package/dist/project/deploy-phases.js +75 -0
  577. package/dist/project/deploy-phases.js.map +1 -0
  578. package/dist/project/exec-in-group.d.ts +49 -0
  579. package/dist/project/exec-in-group.d.ts.map +1 -0
  580. package/dist/project/exec-in-group.js +100 -0
  581. package/dist/project/exec-in-group.js.map +1 -0
  582. package/dist/project/node-app-config.d.ts +101 -0
  583. package/dist/project/node-app-config.d.ts.map +1 -0
  584. package/dist/project/node-app-config.js +134 -0
  585. package/dist/project/node-app-config.js.map +1 -0
  586. package/dist/project/node-app-health.d.ts +76 -0
  587. package/dist/project/node-app-health.d.ts.map +1 -0
  588. package/dist/project/node-app-health.js +119 -0
  589. package/dist/project/node-app-health.js.map +1 -0
  590. package/dist/project/operator-account.d.ts +287 -0
  591. package/dist/project/operator-account.d.ts.map +1 -0
  592. package/dist/project/operator-account.js +373 -0
  593. package/dist/project/operator-account.js.map +1 -0
  594. package/dist/project/registry.d.ts +45 -8
  595. package/dist/project/registry.d.ts.map +1 -1
  596. package/dist/project/registry.js +217 -49
  597. package/dist/project/registry.js.map +1 -1
  598. package/dist/project/requirements-scanner.d.ts +1 -0
  599. package/dist/project/requirements-scanner.d.ts.map +1 -1
  600. package/dist/project/requirements-scanner.js +28 -3
  601. package/dist/project/requirements-scanner.js.map +1 -1
  602. package/dist/project/stack-env.d.ts +37 -0
  603. package/dist/project/stack-env.d.ts.map +1 -0
  604. package/dist/project/stack-env.js +85 -0
  605. package/dist/project/stack-env.js.map +1 -0
  606. package/dist/project/stand-containers.d.ts +4 -0
  607. package/dist/project/stand-containers.d.ts.map +1 -0
  608. package/dist/project/stand-containers.js +27 -0
  609. package/dist/project/stand-containers.js.map +1 -0
  610. package/dist/project/working-tree.d.ts +62 -0
  611. package/dist/project/working-tree.d.ts.map +1 -0
  612. package/dist/project/working-tree.js +101 -0
  613. package/dist/project/working-tree.js.map +1 -0
  614. package/dist/release/artifact-store.d.ts +360 -0
  615. package/dist/release/artifact-store.d.ts.map +1 -0
  616. package/dist/release/artifact-store.js +1040 -0
  617. package/dist/release/artifact-store.js.map +1 -0
  618. package/dist/release/attest-on-node.d.ts +92 -0
  619. package/dist/release/attest-on-node.d.ts.map +1 -0
  620. package/dist/release/attest-on-node.js +207 -0
  621. package/dist/release/attest-on-node.js.map +1 -0
  622. package/dist/release/attest.d.ts +135 -0
  623. package/dist/release/attest.d.ts.map +1 -0
  624. package/dist/release/attest.js +199 -0
  625. package/dist/release/attest.js.map +1 -0
  626. package/dist/release/build-run.d.ts +151 -0
  627. package/dist/release/build-run.d.ts.map +1 -0
  628. package/dist/release/build-run.js +609 -0
  629. package/dist/release/build-run.js.map +1 -0
  630. package/dist/release/builder.d.ts +57 -0
  631. package/dist/release/builder.d.ts.map +1 -0
  632. package/dist/release/builder.js +120 -0
  633. package/dist/release/builder.js.map +1 -0
  634. package/dist/release/daemon-omni.d.ts +41 -0
  635. package/dist/release/daemon-omni.d.ts.map +1 -0
  636. package/dist/release/daemon-omni.js +59 -0
  637. package/dist/release/daemon-omni.js.map +1 -0
  638. package/dist/release/definition-inputs.d.ts +34 -0
  639. package/dist/release/definition-inputs.d.ts.map +1 -0
  640. package/dist/release/definition-inputs.js +90 -0
  641. package/dist/release/definition-inputs.js.map +1 -0
  642. package/dist/release/delivered.d.ts +53 -0
  643. package/dist/release/delivered.d.ts.map +1 -0
  644. package/dist/release/delivered.js +81 -0
  645. package/dist/release/delivered.js.map +1 -0
  646. package/dist/release/layout.d.ts +61 -0
  647. package/dist/release/layout.d.ts.map +1 -0
  648. package/dist/release/layout.js +114 -0
  649. package/dist/release/layout.js.map +1 -0
  650. package/dist/release/load.d.ts +44 -0
  651. package/dist/release/load.d.ts.map +1 -0
  652. package/dist/release/load.js +84 -0
  653. package/dist/release/load.js.map +1 -0
  654. package/dist/release/manifest.d.ts +299 -0
  655. package/dist/release/manifest.d.ts.map +1 -0
  656. package/dist/release/manifest.js +176 -0
  657. package/dist/release/manifest.js.map +1 -0
  658. package/dist/release/migrations.d.ts +32 -0
  659. package/dist/release/migrations.d.ts.map +1 -0
  660. package/dist/release/migrations.js +51 -0
  661. package/dist/release/migrations.js.map +1 -0
  662. package/dist/release/publish.d.ts +71 -0
  663. package/dist/release/publish.d.ts.map +1 -0
  664. package/dist/release/publish.js +102 -0
  665. package/dist/release/publish.js.map +1 -0
  666. package/dist/release/store.d.ts +174 -0
  667. package/dist/release/store.d.ts.map +1 -0
  668. package/dist/release/store.js +257 -0
  669. package/dist/release/store.js.map +1 -0
  670. package/dist/services/alert.rpc-service.d.ts +11 -15
  671. package/dist/services/alert.rpc-service.d.ts.map +1 -1
  672. package/dist/services/alert.rpc-service.js +29 -14
  673. package/dist/services/alert.rpc-service.js.map +1 -1
  674. package/dist/services/alert.service.d.ts +96 -59
  675. package/dist/services/alert.service.d.ts.map +1 -1
  676. package/dist/services/alert.service.js +455 -70
  677. package/dist/services/alert.service.js.map +1 -1
  678. package/dist/services/anonymous-input.d.ts +26 -0
  679. package/dist/services/anonymous-input.d.ts.map +1 -0
  680. package/dist/services/anonymous-input.js +44 -0
  681. package/dist/services/anonymous-input.js.map +1 -0
  682. package/dist/services/audit.rpc-service.d.ts +27 -0
  683. package/dist/services/audit.rpc-service.d.ts.map +1 -0
  684. package/dist/services/audit.rpc-service.js +57 -0
  685. package/dist/services/audit.rpc-service.js.map +1 -0
  686. package/dist/services/audit.service.d.ts +165 -0
  687. package/dist/services/audit.service.d.ts.map +1 -0
  688. package/dist/services/audit.service.js +242 -0
  689. package/dist/services/audit.service.js.map +1 -0
  690. package/dist/services/auth-context.d.ts +21 -1
  691. package/dist/services/auth-context.d.ts.map +1 -1
  692. package/dist/services/auth-context.js +51 -5
  693. package/dist/services/auth-context.js.map +1 -1
  694. package/dist/services/auth.rpc-service.d.ts +0 -1
  695. package/dist/services/auth.rpc-service.d.ts.map +1 -1
  696. package/dist/services/auth.rpc-service.js +23 -9
  697. package/dist/services/auth.rpc-service.js.map +1 -1
  698. package/dist/services/auth.service.d.ts +47 -33
  699. package/dist/services/auth.service.d.ts.map +1 -1
  700. package/dist/services/auth.service.js +155 -23
  701. package/dist/services/auth.service.js.map +1 -1
  702. package/dist/services/backup-pipeline.d.ts +97 -0
  703. package/dist/services/backup-pipeline.d.ts.map +1 -0
  704. package/dist/services/backup-pipeline.js +286 -0
  705. package/dist/services/backup-pipeline.js.map +1 -0
  706. package/dist/services/backup-schedule.d.ts +69 -0
  707. package/dist/services/backup-schedule.d.ts.map +1 -0
  708. package/dist/services/backup-schedule.js +132 -0
  709. package/dist/services/backup-schedule.js.map +1 -0
  710. package/dist/services/backup.rpc-service.d.ts +31 -2
  711. package/dist/services/backup.rpc-service.d.ts.map +1 -1
  712. package/dist/services/backup.rpc-service.js +53 -0
  713. package/dist/services/backup.rpc-service.js.map +1 -1
  714. package/dist/services/backup.service.d.ts +458 -14
  715. package/dist/services/backup.service.d.ts.map +1 -1
  716. package/dist/services/backup.service.js +1368 -170
  717. package/dist/services/backup.service.js.map +1 -1
  718. package/dist/services/bundle-build-worker.d.ts +19 -0
  719. package/dist/services/bundle-build-worker.d.ts.map +1 -0
  720. package/dist/services/bundle-build-worker.js +40 -0
  721. package/dist/services/bundle-build-worker.js.map +1 -0
  722. package/dist/services/bundle-builder.d.ts +487 -0
  723. package/dist/services/bundle-builder.d.ts.map +1 -0
  724. package/dist/services/bundle-builder.js +1083 -0
  725. package/dist/services/bundle-builder.js.map +1 -0
  726. package/dist/services/bundle-worker-protocol.d.ts +19 -0
  727. package/dist/services/bundle-worker-protocol.d.ts.map +1 -0
  728. package/dist/services/bundle-worker-protocol.js +32 -0
  729. package/dist/services/bundle-worker-protocol.js.map +1 -0
  730. package/dist/services/deploy.rpc-service.d.ts +4 -1
  731. package/dist/services/deploy.rpc-service.d.ts.map +1 -1
  732. package/dist/services/deploy.rpc-service.js +10 -0
  733. package/dist/services/deploy.rpc-service.js.map +1 -1
  734. package/dist/services/deploy.service.d.ts +14 -24
  735. package/dist/services/deploy.service.d.ts.map +1 -1
  736. package/dist/services/deploy.service.js +46 -5
  737. package/dist/services/deploy.service.js.map +1 -1
  738. package/dist/services/discovery.rpc-service.d.ts +2 -1
  739. package/dist/services/discovery.rpc-service.d.ts.map +1 -1
  740. package/dist/services/discovery.rpc-service.js.map +1 -1
  741. package/dist/services/discovery.service.d.ts +32 -28
  742. package/dist/services/discovery.service.d.ts.map +1 -1
  743. package/dist/services/discovery.service.js +108 -133
  744. package/dist/services/discovery.service.js.map +1 -1
  745. package/dist/services/event-broadcaster.rpc-service.d.ts +36 -23
  746. package/dist/services/event-broadcaster.rpc-service.d.ts.map +1 -1
  747. package/dist/services/event-broadcaster.rpc-service.js +70 -32
  748. package/dist/services/event-broadcaster.rpc-service.js.map +1 -1
  749. package/dist/services/event-broadcaster.service.d.ts.map +1 -1
  750. package/dist/services/event-broadcaster.service.js.map +1 -1
  751. package/dist/services/fleet.rpc-service.d.ts +2 -1
  752. package/dist/services/fleet.rpc-service.d.ts.map +1 -1
  753. package/dist/services/fleet.rpc-service.js +3 -1
  754. package/dist/services/fleet.rpc-service.js.map +1 -1
  755. package/dist/services/fleet.service.d.ts +32 -34
  756. package/dist/services/fleet.service.d.ts.map +1 -1
  757. package/dist/services/fleet.service.js +66 -10
  758. package/dist/services/fleet.service.js.map +1 -1
  759. package/dist/services/health-check.rpc-service.d.ts +2 -1
  760. package/dist/services/health-check.rpc-service.d.ts.map +1 -1
  761. package/dist/services/health-check.rpc-service.js.map +1 -1
  762. package/dist/services/health-check.service.d.ts +50 -36
  763. package/dist/services/health-check.service.d.ts.map +1 -1
  764. package/dist/services/health-check.service.js +179 -182
  765. package/dist/services/health-check.service.js.map +1 -1
  766. package/dist/services/infrastructure.rpc-service.d.ts +297 -2
  767. package/dist/services/infrastructure.rpc-service.d.ts.map +1 -1
  768. package/dist/services/infrastructure.rpc-service.js +523 -11
  769. package/dist/services/infrastructure.rpc-service.js.map +1 -1
  770. package/dist/services/kubernetes.rpc-service.d.ts +2 -1
  771. package/dist/services/kubernetes.rpc-service.d.ts.map +1 -1
  772. package/dist/services/kubernetes.rpc-service.js.map +1 -1
  773. package/dist/services/kubernetes.service.d.ts +5 -39
  774. package/dist/services/kubernetes.service.d.ts.map +1 -1
  775. package/dist/services/kubernetes.service.js +29 -5
  776. package/dist/services/kubernetes.service.js.map +1 -1
  777. package/dist/services/local-bundle.d.ts +151 -0
  778. package/dist/services/local-bundle.d.ts.map +1 -0
  779. package/dist/services/local-bundle.js +233 -0
  780. package/dist/services/local-bundle.js.map +1 -0
  781. package/dist/services/lockout.d.ts +28 -0
  782. package/dist/services/lockout.d.ts.map +1 -0
  783. package/dist/services/lockout.js +41 -0
  784. package/dist/services/lockout.js.map +1 -0
  785. package/dist/services/log-collector.rpc-service.d.ts +16 -0
  786. package/dist/services/log-collector.rpc-service.d.ts.map +1 -1
  787. package/dist/services/log-collector.rpc-service.js +16 -0
  788. package/dist/services/log-collector.rpc-service.js.map +1 -1
  789. package/dist/services/log-collector.service.d.ts +172 -54
  790. package/dist/services/log-collector.service.d.ts.map +1 -1
  791. package/dist/services/log-collector.service.js +492 -102
  792. package/dist/services/log-collector.service.js.map +1 -1
  793. package/dist/services/log-retention.d.ts +50 -0
  794. package/dist/services/log-retention.d.ts.map +1 -0
  795. package/dist/services/log-retention.js +52 -0
  796. package/dist/services/log-retention.js.map +1 -0
  797. package/dist/services/maintenance-lock.d.ts +162 -0
  798. package/dist/services/maintenance-lock.d.ts.map +1 -0
  799. package/dist/services/maintenance-lock.js +316 -0
  800. package/dist/services/maintenance-lock.js.map +1 -0
  801. package/dist/services/master-address.d.ts +107 -0
  802. package/dist/services/master-address.d.ts.map +1 -0
  803. package/dist/services/master-address.js +154 -0
  804. package/dist/services/master-address.js.map +1 -0
  805. package/dist/services/migration-output.d.ts +16 -0
  806. package/dist/services/migration-output.d.ts.map +1 -0
  807. package/dist/services/migration-output.js +22 -0
  808. package/dist/services/migration-output.js.map +1 -0
  809. package/dist/services/netron-telemetry-transport.d.ts.map +1 -1
  810. package/dist/services/netron-telemetry-transport.js.map +1 -1
  811. package/dist/services/node-deploy-lease.d.ts +154 -0
  812. package/dist/services/node-deploy-lease.d.ts.map +1 -0
  813. package/dist/services/node-deploy-lease.js +386 -0
  814. package/dist/services/node-deploy-lease.js.map +1 -0
  815. package/dist/services/node-health.repository.d.ts +63 -4
  816. package/dist/services/node-health.repository.d.ts.map +1 -1
  817. package/dist/services/node-health.repository.js +108 -67
  818. package/dist/services/node-health.repository.js.map +1 -1
  819. package/dist/services/node-manager.rpc-service.d.ts +355 -10
  820. package/dist/services/node-manager.rpc-service.d.ts.map +1 -1
  821. package/dist/services/node-manager.rpc-service.js +730 -34
  822. package/dist/services/node-manager.rpc-service.js.map +1 -1
  823. package/dist/services/node-manager.service.d.ts +267 -95
  824. package/dist/services/node-manager.service.d.ts.map +1 -1
  825. package/dist/services/node-manager.service.js +805 -103
  826. package/dist/services/node-manager.service.js.map +1 -1
  827. package/dist/services/node-upgrade.d.ts +129 -0
  828. package/dist/services/node-upgrade.d.ts.map +1 -0
  829. package/dist/services/node-upgrade.js +161 -0
  830. package/dist/services/node-upgrade.js.map +1 -0
  831. package/dist/services/node-upgrade.service.d.ts +232 -0
  832. package/dist/services/node-upgrade.service.d.ts.map +1 -0
  833. package/dist/services/node-upgrade.service.js +468 -0
  834. package/dist/services/node-upgrade.service.js.map +1 -0
  835. package/dist/services/pipeline.rpc-service.d.ts +2 -1
  836. package/dist/services/pipeline.rpc-service.d.ts.map +1 -1
  837. package/dist/services/pipeline.rpc-service.js.map +1 -1
  838. package/dist/services/pipeline.service.d.ts +15 -46
  839. package/dist/services/pipeline.service.d.ts.map +1 -1
  840. package/dist/services/pipeline.service.js +72 -9
  841. package/dist/services/pipeline.service.js.map +1 -1
  842. package/dist/services/project.rpc-service.d.ts +133 -5
  843. package/dist/services/project.rpc-service.d.ts.map +1 -1
  844. package/dist/services/project.rpc-service.js +249 -22
  845. package/dist/services/project.rpc-service.js.map +1 -1
  846. package/dist/services/project.service.d.ts +677 -15
  847. package/dist/services/project.service.d.ts.map +1 -1
  848. package/dist/services/project.service.js +2798 -304
  849. package/dist/services/project.service.js.map +1 -1
  850. package/dist/services/redeploy-decision.d.ts +176 -0
  851. package/dist/services/redeploy-decision.d.ts.map +1 -0
  852. package/dist/services/redeploy-decision.js +142 -0
  853. package/dist/services/redeploy-decision.js.map +1 -0
  854. package/dist/services/release.rpc-service.d.ts +159 -0
  855. package/dist/services/release.rpc-service.d.ts.map +1 -0
  856. package/dist/services/release.rpc-service.js +395 -0
  857. package/dist/services/release.rpc-service.js.map +1 -0
  858. package/dist/services/release.service.d.ts +114 -0
  859. package/dist/services/release.service.d.ts.map +1 -0
  860. package/dist/services/release.service.js +208 -0
  861. package/dist/services/release.service.js.map +1 -0
  862. package/dist/services/remote-deployer.service.d.ts +569 -8
  863. package/dist/services/remote-deployer.service.d.ts.map +1 -1
  864. package/dist/services/remote-deployer.service.js +1618 -169
  865. package/dist/services/remote-deployer.service.js.map +1 -1
  866. package/dist/services/remote-ops.service.d.ts +51 -9
  867. package/dist/services/remote-ops.service.d.ts.map +1 -1
  868. package/dist/services/remote-ops.service.js +235 -39
  869. package/dist/services/remote-ops.service.js.map +1 -1
  870. package/dist/services/remote-provisioner.d.ts +122 -0
  871. package/dist/services/remote-provisioner.d.ts.map +1 -0
  872. package/dist/services/remote-provisioner.js +258 -0
  873. package/dist/services/remote-provisioner.js.map +1 -0
  874. package/dist/services/secrets.rpc-service.d.ts +51 -2
  875. package/dist/services/secrets.rpc-service.d.ts.map +1 -1
  876. package/dist/services/secrets.rpc-service.js +81 -2
  877. package/dist/services/secrets.rpc-service.js.map +1 -1
  878. package/dist/services/secrets.service.d.ts +108 -9
  879. package/dist/services/secrets.service.d.ts.map +1 -1
  880. package/dist/services/secrets.service.js +313 -48
  881. package/dist/services/secrets.service.js.map +1 -1
  882. package/dist/services/slave-storage.service.d.ts +107 -0
  883. package/dist/services/slave-storage.service.d.ts.map +1 -1
  884. package/dist/services/slave-storage.service.js +171 -2
  885. package/dist/services/slave-storage.service.js.map +1 -1
  886. package/dist/services/static-carry-forward.d.ts +85 -0
  887. package/dist/services/static-carry-forward.d.ts.map +1 -0
  888. package/dist/services/static-carry-forward.js +159 -0
  889. package/dist/services/static-carry-forward.js.map +1 -0
  890. package/dist/services/sync-policy.d.ts +167 -0
  891. package/dist/services/sync-policy.d.ts.map +1 -0
  892. package/dist/services/sync-policy.js +161 -0
  893. package/dist/services/sync-policy.js.map +1 -0
  894. package/dist/services/sync-summary.d.ts +39 -0
  895. package/dist/services/sync-summary.d.ts.map +1 -0
  896. package/dist/services/sync-summary.js +62 -0
  897. package/dist/services/sync-summary.js.map +1 -0
  898. package/dist/services/sync.rpc-service.d.ts +19 -7
  899. package/dist/services/sync.rpc-service.d.ts.map +1 -1
  900. package/dist/services/sync.rpc-service.js +36 -5
  901. package/dist/services/sync.rpc-service.js.map +1 -1
  902. package/dist/services/sync.service.d.ts +350 -18
  903. package/dist/services/sync.service.d.ts.map +1 -1
  904. package/dist/services/sync.service.js +864 -116
  905. package/dist/services/sync.service.js.map +1 -1
  906. package/dist/services/system-info.rpc-service.d.ts +2 -1
  907. package/dist/services/system-info.rpc-service.d.ts.map +1 -1
  908. package/dist/services/system-info.rpc-service.js.map +1 -1
  909. package/dist/services/system-info.service.d.ts +56 -68
  910. package/dist/services/system-info.service.d.ts.map +1 -1
  911. package/dist/services/system-info.service.js +240 -127
  912. package/dist/services/system-info.service.js.map +1 -1
  913. package/dist/services/telemetry.rpc-service.d.ts.map +1 -1
  914. package/dist/services/telemetry.rpc-service.js +9 -3
  915. package/dist/services/telemetry.rpc-service.js.map +1 -1
  916. package/dist/services/telemetry.service.d.ts +4 -2
  917. package/dist/services/telemetry.service.d.ts.map +1 -1
  918. package/dist/services/telemetry.service.js +4 -2
  919. package/dist/services/telemetry.service.js.map +1 -1
  920. package/dist/services/trace-collector.rpc-service.d.ts +2 -1
  921. package/dist/services/trace-collector.rpc-service.d.ts.map +1 -1
  922. package/dist/services/trace-collector.rpc-service.js.map +1 -1
  923. package/dist/services/trace-collector.service.d.ts +2 -41
  924. package/dist/services/trace-collector.service.d.ts.map +1 -1
  925. package/dist/services/trace-collector.service.js +27 -7
  926. package/dist/services/trace-collector.service.js.map +1 -1
  927. package/dist/services/wait-for-postgres.d.ts +28 -0
  928. package/dist/services/wait-for-postgres.d.ts.map +1 -0
  929. package/dist/services/wait-for-postgres.js +77 -0
  930. package/dist/services/wait-for-postgres.js.map +1 -0
  931. package/dist/shared/alert-expression.d.ts +60 -0
  932. package/dist/shared/alert-expression.d.ts.map +1 -0
  933. package/dist/shared/alert-expression.js +124 -0
  934. package/dist/shared/alert-expression.js.map +1 -0
  935. package/dist/shared/audit-outcome.d.ts +39 -0
  936. package/dist/shared/audit-outcome.d.ts.map +1 -0
  937. package/dist/shared/audit-outcome.js +45 -0
  938. package/dist/shared/audit-outcome.js.map +1 -0
  939. package/dist/shared/build-freshness.d.ts +59 -0
  940. package/dist/shared/build-freshness.d.ts.map +1 -0
  941. package/dist/shared/build-freshness.js +113 -0
  942. package/dist/shared/build-freshness.js.map +1 -0
  943. package/dist/shared/build-stamp.d.ts +26 -0
  944. package/dist/shared/build-stamp.d.ts.map +1 -0
  945. package/dist/shared/build-stamp.js +36 -0
  946. package/dist/shared/build-stamp.js.map +1 -0
  947. package/dist/shared/describe-error.d.ts +22 -0
  948. package/dist/shared/describe-error.d.ts.map +1 -0
  949. package/dist/shared/describe-error.js +39 -0
  950. package/dist/shared/describe-error.js.map +1 -0
  951. package/dist/shared/dto/alerts.d.ts +101 -0
  952. package/dist/shared/dto/alerts.d.ts.map +1 -0
  953. package/dist/shared/dto/alerts.js +9 -0
  954. package/dist/shared/dto/alerts.js.map +1 -0
  955. package/dist/shared/dto/auth.d.ts +49 -0
  956. package/dist/shared/dto/auth.d.ts.map +1 -0
  957. package/dist/shared/dto/auth.js +14 -0
  958. package/dist/shared/dto/auth.js.map +1 -0
  959. package/dist/shared/dto/backups.d.ts +16 -0
  960. package/dist/shared/dto/backups.d.ts.map +1 -0
  961. package/dist/shared/dto/backups.js +9 -0
  962. package/dist/shared/dto/backups.js.map +1 -0
  963. package/dist/shared/dto/deploy.d.ts +28 -0
  964. package/dist/shared/dto/deploy.d.ts.map +1 -0
  965. package/dist/shared/dto/deploy.js +8 -0
  966. package/dist/shared/dto/deploy.js.map +1 -0
  967. package/dist/shared/dto/discovery.d.ts +24 -0
  968. package/dist/shared/dto/discovery.d.ts.map +1 -0
  969. package/dist/shared/dto/discovery.js +9 -0
  970. package/dist/shared/dto/discovery.js.map +1 -0
  971. package/dist/shared/dto/fleet.d.ts +35 -0
  972. package/dist/shared/dto/fleet.d.ts.map +1 -0
  973. package/dist/shared/dto/fleet.js +8 -0
  974. package/dist/shared/dto/fleet.js.map +1 -0
  975. package/dist/shared/dto/health.d.ts +26 -0
  976. package/dist/shared/dto/health.d.ts.map +1 -0
  977. package/dist/shared/dto/health.js +9 -0
  978. package/dist/shared/dto/health.js.map +1 -0
  979. package/dist/shared/dto/kubernetes.d.ts +44 -0
  980. package/dist/shared/dto/kubernetes.d.ts.map +1 -0
  981. package/dist/shared/dto/kubernetes.js +9 -0
  982. package/dist/shared/dto/kubernetes.js.map +1 -0
  983. package/dist/shared/dto/logs.d.ts +77 -0
  984. package/dist/shared/dto/logs.d.ts.map +1 -0
  985. package/dist/shared/dto/logs.js +10 -0
  986. package/dist/shared/dto/logs.js.map +1 -0
  987. package/dist/shared/dto/metrics.d.ts +65 -0
  988. package/dist/shared/dto/metrics.d.ts.map +1 -0
  989. package/dist/shared/dto/metrics.js +17 -0
  990. package/dist/shared/dto/metrics.js.map +1 -0
  991. package/dist/shared/dto/nodes.d.ts +347 -0
  992. package/dist/shared/dto/nodes.d.ts.map +1 -0
  993. package/dist/shared/dto/nodes.js +8 -0
  994. package/dist/shared/dto/nodes.js.map +1 -0
  995. package/dist/shared/dto/pipelines.d.ts +53 -0
  996. package/dist/shared/dto/pipelines.d.ts.map +1 -0
  997. package/dist/shared/dto/pipelines.js +9 -0
  998. package/dist/shared/dto/pipelines.js.map +1 -0
  999. package/dist/shared/dto/project.d.ts +142 -8
  1000. package/dist/shared/dto/project.d.ts.map +1 -1
  1001. package/dist/shared/dto/services.d.ts +831 -7
  1002. package/dist/shared/dto/services.d.ts.map +1 -1
  1003. package/dist/shared/dto/system-info.d.ts +93 -0
  1004. package/dist/shared/dto/system-info.d.ts.map +1 -0
  1005. package/dist/shared/dto/system-info.js +9 -0
  1006. package/dist/shared/dto/system-info.js.map +1 -0
  1007. package/dist/shared/dto/traces.d.ts +49 -0
  1008. package/dist/shared/dto/traces.d.ts.map +1 -0
  1009. package/dist/shared/dto/traces.js +9 -0
  1010. package/dist/shared/dto/traces.js.map +1 -0
  1011. package/dist/shared/env-config.d.ts +74 -0
  1012. package/dist/shared/env-config.d.ts.map +1 -0
  1013. package/dist/shared/env-config.js +99 -0
  1014. package/dist/shared/env-config.js.map +1 -0
  1015. package/dist/shared/events.d.ts +45 -39
  1016. package/dist/shared/events.d.ts.map +1 -1
  1017. package/dist/shared/events.js +6 -0
  1018. package/dist/shared/events.js.map +1 -1
  1019. package/dist/shared/format.d.ts +1 -1
  1020. package/dist/shared/format.d.ts.map +1 -1
  1021. package/dist/shared/format.js +4 -0
  1022. package/dist/shared/format.js.map +1 -1
  1023. package/dist/shared/local-infra-host.d.ts +28 -0
  1024. package/dist/shared/local-infra-host.d.ts.map +1 -0
  1025. package/dist/shared/local-infra-host.js +28 -0
  1026. package/dist/shared/local-infra-host.js.map +1 -0
  1027. package/dist/shared/node-check.d.ts +37 -0
  1028. package/dist/shared/node-check.d.ts.map +1 -0
  1029. package/dist/shared/node-check.js +41 -0
  1030. package/dist/shared/node-check.js.map +1 -0
  1031. package/dist/shared/paths.d.ts +29 -0
  1032. package/dist/shared/paths.d.ts.map +1 -0
  1033. package/dist/shared/paths.js +54 -0
  1034. package/dist/shared/paths.js.map +1 -0
  1035. package/dist/shared/pnpm.d.ts +29 -0
  1036. package/dist/shared/pnpm.d.ts.map +1 -0
  1037. package/dist/shared/pnpm.js +57 -0
  1038. package/dist/shared/pnpm.js.map +1 -0
  1039. package/dist/shared/private-files.d.ts +29 -0
  1040. package/dist/shared/private-files.d.ts.map +1 -0
  1041. package/dist/shared/private-files.js +109 -0
  1042. package/dist/shared/private-files.js.map +1 -0
  1043. package/dist/shared/redact-env.d.ts +24 -0
  1044. package/dist/shared/redact-env.d.ts.map +1 -0
  1045. package/dist/shared/redact-env.js +36 -0
  1046. package/dist/shared/redact-env.js.map +1 -0
  1047. package/dist/shared/release-reading.d.ts +85 -0
  1048. package/dist/shared/release-reading.d.ts.map +1 -0
  1049. package/dist/shared/release-reading.js +81 -0
  1050. package/dist/shared/release-reading.js.map +1 -0
  1051. package/dist/shared/roles.d.ts +58 -3
  1052. package/dist/shared/roles.d.ts.map +1 -1
  1053. package/dist/shared/roles.js +55 -0
  1054. package/dist/shared/roles.js.map +1 -1
  1055. package/dist/shared/rpcauth.d.ts +13 -0
  1056. package/dist/shared/rpcauth.d.ts.map +1 -0
  1057. package/dist/shared/rpcauth.js +23 -0
  1058. package/dist/shared/rpcauth.js.map +1 -0
  1059. package/dist/shared/shell-escape.d.ts +9 -0
  1060. package/dist/shared/shell-escape.d.ts.map +1 -0
  1061. package/dist/shared/shell-escape.js +11 -0
  1062. package/dist/shared/shell-escape.js.map +1 -0
  1063. package/dist/shared/sync-reading.d.ts +45 -0
  1064. package/dist/shared/sync-reading.d.ts.map +1 -0
  1065. package/dist/shared/sync-reading.js +42 -0
  1066. package/dist/shared/sync-reading.js.map +1 -0
  1067. package/dist/shared/tokens.d.ts +19 -0
  1068. package/dist/shared/tokens.d.ts.map +1 -1
  1069. package/dist/shared/tokens.js +28 -0
  1070. package/dist/shared/tokens.js.map +1 -1
  1071. package/dist/webapp/webapp.service.d.ts +33 -2
  1072. package/dist/webapp/webapp.service.d.ts.map +1 -1
  1073. package/dist/webapp/webapp.service.js +143 -31
  1074. package/dist/webapp/webapp.service.js.map +1 -1
  1075. package/dist/workers/health-monitor-process.d.ts.map +1 -1
  1076. package/dist/workers/health-monitor-process.js +10 -0
  1077. package/dist/workers/health-monitor-process.js.map +1 -1
  1078. package/dist/workers/health-monitor.service.d.ts +45 -3
  1079. package/dist/workers/health-monitor.service.d.ts.map +1 -1
  1080. package/dist/workers/health-monitor.service.js +152 -25
  1081. package/dist/workers/health-monitor.service.js.map +1 -1
  1082. package/dist/workers/system-worker-manager.d.ts +43 -0
  1083. package/dist/workers/system-worker-manager.d.ts.map +1 -1
  1084. package/dist/workers/system-worker-manager.js +150 -3
  1085. package/dist/workers/system-worker-manager.js.map +1 -1
  1086. package/dist/workers/types.d.ts +8 -2
  1087. package/dist/workers/types.d.ts.map +1 -1
  1088. package/package.json +64 -30
  1089. package/webapp/dist/BUILD.json +5 -0
  1090. package/webapp/dist/assets/FormControlLabel-BrH2diyl.js +1 -0
  1091. package/webapp/dist/assets/Grid-BZ7xhoHQ.js +1 -0
  1092. package/webapp/dist/assets/Switch-DGSAxQPM.js +1 -0
  1093. package/webapp/dist/assets/alerts-sXqHJbsa.js +1 -0
  1094. package/webapp/dist/assets/apps-B_Ah8Flh.js +1 -0
  1095. package/webapp/dist/assets/audit-YXihwyui.js +1 -0
  1096. package/webapp/dist/assets/client-CGbBvy9k.js +1 -0
  1097. package/webapp/dist/assets/constants-veKGFkC6.js +1 -0
  1098. package/webapp/dist/assets/containers-Di4NOTLf.js +2 -0
  1099. package/webapp/dist/assets/daemon-memory-CCPZ4cV_.js +1 -0
  1100. package/webapp/dist/assets/dashboard-Cfuf95N3.js +1 -0
  1101. package/webapp/dist/assets/dashboard-builder-CowaYb-M.js +1 -0
  1102. package/webapp/dist/assets/deploy-release-dialog-D6C-XcKY.js +1 -0
  1103. package/webapp/dist/assets/deployments-ChTq5kCw.js +1 -0
  1104. package/webapp/dist/assets/detail-BsGWv7Dj.js +1 -0
  1105. package/webapp/dist/assets/detail-CyVf_cw5.js +2 -0
  1106. package/webapp/dist/assets/detail-D027j6aO.js +8 -0
  1107. package/webapp/dist/assets/formatters-BqjL73N3.js +1 -0
  1108. package/webapp/dist/assets/index-B8AHIPc-.css +1 -0
  1109. package/webapp/dist/assets/index-DiZbrwxT.js +414 -0
  1110. package/webapp/dist/assets/logs-L2b8Oqmo.js +12 -0
  1111. package/webapp/dist/assets/maplibre-gl-MlmG2kbn.js +801 -0
  1112. package/webapp/dist/assets/metrics-CQgjz2Lz.js +2 -0
  1113. package/webapp/dist/assets/nodes-Nwia2Ty6.js +7 -0
  1114. package/webapp/dist/assets/pipelines-DCu_rXOr.js +4 -0
  1115. package/webapp/dist/assets/projects-Bb4aV76h.js +1 -0
  1116. package/webapp/dist/assets/react-CwJFpaho.js +1 -0
  1117. package/webapp/dist/assets/react-apexcharts.esm-Dy0Fez42.js +1956 -0
  1118. package/webapp/dist/assets/release-bits-Dj4EOhhb.js +1 -0
  1119. package/webapp/dist/assets/release-build-panel-D6OCVlhT.js +1 -0
  1120. package/webapp/dist/assets/releases-CnAt3yO6.js +2 -0
  1121. package/webapp/dist/assets/rolldown-runtime-hePW80VL.js +1 -0
  1122. package/webapp/dist/assets/settings-DU-mjiwb.js +1 -0
  1123. package/webapp/dist/assets/settled-pair-C7SjZuns.js +1 -0
  1124. package/webapp/dist/assets/sign-in-Bcvjrr4C.js +1 -0
  1125. package/webapp/dist/assets/stacks-B-e8p50I.js +1 -0
  1126. package/webapp/dist/assets/sync-reading-C3PnJ7AX.js +1 -0
  1127. package/webapp/dist/assets/system-info-BtE3dgfw.js +1 -0
  1128. package/webapp/dist/assets/topology-Cwch-uF1.js +12 -0
  1129. package/webapp/dist/assets/topology-DLioOiRN.css +1 -0
  1130. package/webapp/dist/assets/traces-BbShR-4P.js +1 -0
  1131. package/webapp/dist/assets/use-stack-context-CZUgPq88.js +1 -0
  1132. package/webapp/dist/index.html +6 -6
  1133. package/dist/commands/dev.d.ts +0 -23
  1134. package/dist/commands/dev.d.ts.map +0 -1
  1135. package/dist/commands/dev.js +0 -76
  1136. package/dist/commands/dev.js.map +0 -1
  1137. package/dist/config/define-app.d.ts +0 -6
  1138. package/dist/config/define-app.d.ts.map +0 -1
  1139. package/dist/config/define-app.js +0 -38
  1140. package/dist/config/define-app.js.map +0 -1
  1141. package/dist/infrastructure/gateway-generator.d.ts +0 -63
  1142. package/dist/infrastructure/gateway-generator.d.ts.map +0 -1
  1143. package/dist/infrastructure/gateway-generator.js +0 -253
  1144. package/dist/infrastructure/gateway-generator.js.map +0 -1
  1145. package/dist/monitoring/health.service.d.ts +0 -14
  1146. package/dist/monitoring/health.service.d.ts.map +0 -1
  1147. package/dist/monitoring/health.service.js +0 -50
  1148. package/dist/monitoring/health.service.js.map +0 -1
  1149. package/dist/monitoring/metrics.service.d.ts +0 -14
  1150. package/dist/monitoring/metrics.service.d.ts.map +0 -1
  1151. package/dist/monitoring/metrics.service.js +0 -40
  1152. package/dist/monitoring/metrics.service.js.map +0 -1
  1153. package/dist/orchestrator/bootstrap-runner.d.ts +0 -15
  1154. package/dist/orchestrator/bootstrap-runner.d.ts.map +0 -1
  1155. package/dist/orchestrator/bootstrap-runner.js +0 -152
  1156. package/dist/orchestrator/bootstrap-runner.js.map +0 -1
  1157. package/dist/orchestrator/topology-proxy.d.ts +0 -65
  1158. package/dist/orchestrator/topology-proxy.d.ts.map +0 -1
  1159. package/dist/orchestrator/topology-proxy.js +0 -209
  1160. package/dist/orchestrator/topology-proxy.js.map +0 -1
  1161. package/dist/orchestrator/topology-relay.d.ts +0 -111
  1162. package/dist/orchestrator/topology-relay.d.ts.map +0 -1
  1163. package/dist/orchestrator/topology-relay.js +0 -255
  1164. package/dist/orchestrator/topology-relay.js.map +0 -1
  1165. package/dist/services/auth-middleware.d.ts +0 -47
  1166. package/dist/services/auth-middleware.d.ts.map +0 -1
  1167. package/dist/services/auth-middleware.js +0 -158
  1168. package/dist/services/auth-middleware.js.map +0 -1
  1169. package/dist/services/environment.rpc-service.d.ts +0 -22
  1170. package/dist/services/environment.rpc-service.d.ts.map +0 -1
  1171. package/dist/services/environment.rpc-service.js +0 -65
  1172. package/dist/services/environment.rpc-service.js.map +0 -1
  1173. package/dist/services/environment.service.d.ts +0 -87
  1174. package/dist/services/environment.service.d.ts.map +0 -1
  1175. package/dist/services/environment.service.js +0 -220
  1176. package/dist/services/environment.service.js.map +0 -1
  1177. package/dist/services/metrics-collector.service.d.ts +0 -103
  1178. package/dist/services/metrics-collector.service.d.ts.map +0 -1
  1179. package/dist/services/metrics-collector.service.js +0 -235
  1180. package/dist/services/metrics-collector.service.js.map +0 -1
  1181. package/dist/services/metrics.rpc-service.d.ts +0 -41
  1182. package/dist/services/metrics.rpc-service.d.ts.map +0 -1
  1183. package/dist/services/metrics.rpc-service.js +0 -90
  1184. package/dist/services/metrics.rpc-service.js.map +0 -1
  1185. package/dist/services/metrics.service.d.ts +0 -72
  1186. package/dist/services/metrics.service.d.ts.map +0 -1
  1187. package/dist/services/metrics.service.js +0 -185
  1188. package/dist/services/metrics.service.js.map +0 -1
  1189. package/dist/supervisor/omnitron-supervisor.d.ts +0 -21
  1190. package/dist/supervisor/omnitron-supervisor.d.ts.map +0 -1
  1191. package/dist/supervisor/omnitron-supervisor.js +0 -43
  1192. package/dist/supervisor/omnitron-supervisor.js.map +0 -1
  1193. package/dist/webapp/static-server.d.ts +0 -24
  1194. package/dist/webapp/static-server.d.ts.map +0 -1
  1195. package/dist/webapp/static-server.js +0 -152
  1196. package/dist/webapp/static-server.js.map +0 -1
  1197. package/webapp/dist/assets/Badge-D2ZBP5iK.js +0 -1
  1198. package/webapp/dist/assets/Card-vO88g06B.js +0 -1
  1199. package/webapp/dist/assets/CardActions-C9h7Q2wW.js +0 -1
  1200. package/webapp/dist/assets/CardContent-BA_Xmmhk.js +0 -1
  1201. package/webapp/dist/assets/CardHeader-ceIs__6x.js +0 -1
  1202. package/webapp/dist/assets/Chip-Ckk0xUhK.js +0 -1
  1203. package/webapp/dist/assets/DialogActions-BhHM7hov.js +0 -1
  1204. package/webapp/dist/assets/DialogContentText-BT2JQRmK.js +0 -1
  1205. package/webapp/dist/assets/FormControlLabel-CcQByVS8.js +0 -1
  1206. package/webapp/dist/assets/Grid-14PETRQW.js +0 -1
  1207. package/webapp/dist/assets/InputAdornment-B8aHIsQ7.js +0 -1
  1208. package/webapp/dist/assets/Skeleton-B74-Ds6O.js +0 -37
  1209. package/webapp/dist/assets/Switch-DvWD2wh4.js +0 -1
  1210. package/webapp/dist/assets/SwitchBase-CDU4nY8O.js +0 -1
  1211. package/webapp/dist/assets/Tab-DcCOYYp5.js +0 -1
  1212. package/webapp/dist/assets/TableContainer-CzTh4mu-.js +0 -1
  1213. package/webapp/dist/assets/TableRow-BySEreZR.js +0 -2
  1214. package/webapp/dist/assets/TextField-BLf-BHz2.js +0 -1
  1215. package/webapp/dist/assets/alerts-DnRbT41C.js +0 -1
  1216. package/webapp/dist/assets/apps-CdR0VH2d.js +0 -1
  1217. package/webapp/dist/assets/chunk-DtcAfS_W.js +0 -1
  1218. package/webapp/dist/assets/client-DU8I_lDc.js +0 -2
  1219. package/webapp/dist/assets/constants-BgnT-Xdy.js +0 -1
  1220. package/webapp/dist/assets/containers-Dw-rRXV1.js +0 -1
  1221. package/webapp/dist/assets/dashboard-CzgB0uZz.js +0 -1
  1222. package/webapp/dist/assets/dashboard-builder-DsA0yg33.js +0 -1
  1223. package/webapp/dist/assets/deployments-DtQAoIcQ.js +0 -1
  1224. package/webapp/dist/assets/detail-BJR-G7JG.js +0 -1
  1225. package/webapp/dist/assets/detail-nlCeSltl.js +0 -8
  1226. package/webapp/dist/assets/formatters-Do41SRmE.js +0 -1
  1227. package/webapp/dist/assets/index-ByQDwp2r.js +0 -566
  1228. package/webapp/dist/assets/index-CHVXOsTS.css +0 -1
  1229. package/webapp/dist/assets/isMuiElement-DwF2naaF.js +0 -1
  1230. package/webapp/dist/assets/logs-Kn8o4H7k.js +0 -11
  1231. package/webapp/dist/assets/metrics-DN8Zu5wT.js +0 -2
  1232. package/webapp/dist/assets/nodes-Bgch7AtN.js +0 -7
  1233. package/webapp/dist/assets/pipelines-Cry3qm-S.js +0 -4
  1234. package/webapp/dist/assets/popper-BJFSj6rc.js +0 -1
  1235. package/webapp/dist/assets/preload-helper-DSXbuxSR.js +0 -1
  1236. package/webapp/dist/assets/projects-Dym2GI1U.js +0 -1
  1237. package/webapp/dist/assets/react-apexcharts.esm-CSGK5Cxn.js +0 -958
  1238. package/webapp/dist/assets/react-apexcharts.esm-DEAWOwx5.js +0 -1003
  1239. package/webapp/dist/assets/settings-va3zTcow.js +0 -1
  1240. package/webapp/dist/assets/sign-in-ik35umbp.js +0 -1
  1241. package/webapp/dist/assets/stacks-CVFOnxbF.js +0 -1
  1242. package/webapp/dist/assets/system-info-npIXAjX2.js +0 -1
  1243. package/webapp/dist/assets/topology-CHpVij2M.css +0 -1
  1244. package/webapp/dist/assets/topology-bDe70vLs.js +0 -12
  1245. package/webapp/dist/assets/traces-DIIcT8DF.js +0 -1
  1246. package/webapp/dist/assets/use-stack-context-DHLwZv1r.js +0 -1
  1247. /package/webapp/dist/assets/{fs-CgZLtHiQ.js → fs-Ccl3sAcp.js} +0 -0
@@ -43,23 +43,191 @@ import { FileWatcher } from '../orchestrator/file-watcher.js';
43
43
  import { InfrastructureService } from '../infrastructure/infrastructure.service.js';
44
44
  import { DockerHealthIndicator } from '../monitoring/docker-health.indicator.js';
45
45
  import { AppHealthIndicator } from '../monitoring/app-health.indicator.js';
46
+ import { EventLoopWatch } from '../monitoring/event-loop-watch.js';
47
+ import { EventLoopStallIndicator } from '../monitoring/event-loop-stall.indicator.js';
46
48
  import { registerDaemonJobs } from './daemon-scheduler.js';
47
49
  import { METRICS_SERVICE_TOKEN as TITAN_METRICS_TOKEN } from '@omnitron-dev/titan-metrics';
48
- import { ORCHESTRATOR_TOKEN, LOG_MANAGER_TOKEN, STATE_STORE_TOKEN, OMNITRON_DB_TOKEN, AUTH_SERVICE_TOKEN, LOG_COLLECTOR_TOKEN, FLEET_SERVICE_TOKEN, ALERT_SERVICE_TOKEN, HEALTH_CHECK_SERVICE_TOKEN, DISCOVERY_SERVICE_TOKEN, KUBERNETES_SERVICE_TOKEN, PIPELINE_SERVICE_TOKEN, TRACE_COLLECTOR_TOKEN, BACKUP_SERVICE_TOKEN, SECRETS_SERVICE_TOKEN, TELEMETRY_RELAY_TOKEN, DEPLOY_SERVICE_TOKEN, INFRASTRUCTURE_GATE_TOKEN, PROJECT_SERVICE_TOKEN, SLAVE_STORAGE_TOKEN, } from '../shared/tokens.js';
50
+ import { ORCHESTRATOR_TOKEN, LOG_MANAGER_TOKEN, STATE_STORE_TOKEN, OMNITRON_DB_TOKEN, AUTH_SERVICE_TOKEN, LOG_COLLECTOR_TOKEN, FLEET_SERVICE_TOKEN, ALERT_SERVICE_TOKEN, HEALTH_CHECK_SERVICE_TOKEN, DISCOVERY_SERVICE_TOKEN, KUBERNETES_SERVICE_TOKEN, PIPELINE_SERVICE_TOKEN, TRACE_COLLECTOR_TOKEN, BACKUP_SERVICE_TOKEN, SECRETS_SERVICE_TOKEN, AUDIT_SERVICE_TOKEN, DAEMON_STATE_STORE_TOKEN, TELEMETRY_RELAY_TOKEN, DEPLOY_SERVICE_TOKEN, INFRASTRUCTURE_GATE_TOKEN, PROJECT_SERVICE_TOKEN, SLAVE_STORAGE_TOKEN, } from '../shared/tokens.js';
49
51
  import { APP_EVENTS, INFRA_EVENTS, DAEMON_EVENTS, PROJECT_EVENTS, STACK_EVENTS, NODE_EVENTS, } from '../shared/events.js';
50
52
  import { JWT_SERVICE_TOKEN } from '@omnitron-dev/titan-auth';
51
- import { AuthenticationManager } from '@omnitron-dev/titan/netron/auth';
53
+ import { AuthenticationManager, AuthorizationManager, } from '@omnitron-dev/titan/netron/auth';
52
54
  import { createAuthContextWrapper } from '../services/auth-context.js';
55
+ import { ROLES } from '../shared/roles.js';
56
+ import { expandPath } from '../shared/paths.js';
57
+ /**
58
+ * Deadline for a request on the daemon's unix socket.
59
+ *
60
+ * Same value as `CLI_REQUEST_TIMEOUT` in `daemon-client.ts`: one number for
61
+ * one socket, stated by both ends rather than inherited from a default meant
62
+ * for wire requests.
63
+ */
64
+ const DAEMON_SOCKET_REQUEST_TIMEOUT = 60_000;
65
+ /**
66
+ * Where the daemon listens. The default is loopback; anything the operator
67
+ * writes is passed through literally, `0.0.0.0` included.
68
+ *
69
+ * Exported so the two call sites below and the tests agree on one
70
+ * definition. They previously each carried their own copy of
71
+ * `host !== '0.0.0.0' ? host : '127.0.0.1'`, which is how the two of them
72
+ * plus the config default managed to disagree about what the default was.
73
+ */
74
+ export function resolveBindHost(configured) {
75
+ return configured ?? '127.0.0.1';
76
+ }
77
+ /**
78
+ * Where a daemon binds its fleet TCP transport.
79
+ *
80
+ * A master follows `daemon.host`, whose default is loopback — the safe answer
81
+ * for the machine an operator is sitting at.
82
+ *
83
+ * A slave cannot use that default, because being reachable is the whole of
84
+ * what a slave is for: the master dials it to check its health and to pull
85
+ * from it. Bound to loopback it is a daemon nobody can see, and the console
86
+ * shows it as offline for ever with no way to tell it from a machine that is
87
+ * genuinely down.
88
+ *
89
+ * It was exactly that, and by a longer route than a wrong default. The
90
+ * generated slave config carried `daemon: { host: '0.0.0.0', … }` — and
91
+ * `IEcosystemConfig`, the schema of the file it was written into, has no
92
+ * `daemon` key; the daemon boots from `~/.omnitron/config.json`, which until
93
+ * now could not carry one either. So the setting was written, ignored, and
94
+ * every provisioned slave would have bound 127.0.0.1.
95
+ *
96
+ * An explicit `host` still wins, for an operator who wants one interface.
97
+ */
98
+ export function fleetBindHostFor(dc) {
99
+ if (dc.role === 'slave')
100
+ return dc.host ?? '0.0.0.0';
101
+ return resolveBindHost(dc.host);
102
+ }
103
+ /**
104
+ * The bind address a daemon should END UP with, from what was actually
105
+ * configured.
106
+ *
107
+ * `fleetBindHostFor` above takes the daemon's RUNNING config, and by the time
108
+ * anything holds one, `host` has been filled in from `DEFAULT_DAEMON_CONFIG`
109
+ * — so `?? '0.0.0.0'` can never fire and every slave binds loopback. The
110
+ * information it needs is whether an operator SET a host, and only the saved
111
+ * file knows that: absent means "no opinion", `127.0.0.1` means "loopback,
112
+ * deliberately".
113
+ *
114
+ * Measured 2026-09-14 on a provisioned slave, after the rule was written and
115
+ * before this existed:
116
+ *
117
+ * LISTEN 127.0.0.1:9700 ← the fleet port no master can dial
118
+ * LISTEN 127.0.0.1:9801
119
+ * LISTEN 127.0.0.1:9802
120
+ *
121
+ * The rule was right, the value it was given was already decided. This is the
122
+ * same rule applied where the answer is still open.
123
+ */
124
+ export function effectiveBindHost(saved) {
125
+ if (saved?.host)
126
+ return saved.host;
127
+ if (saved?.role === 'slave')
128
+ return '0.0.0.0';
129
+ return DEFAULT_DAEMON_CONFIG.host;
130
+ }
131
+ /**
132
+ * Where a daemon binds the surfaces that exist to serve a console: the Netron
133
+ * HTTP RPC and the WebSocket.
134
+ *
135
+ * A master follows `daemon.host`, like everything else. A slave does not,
136
+ * because on a slave that knob is published for a different reason: a fleet
137
+ * node has to bind its TCP transport somewhere the master can reach, and a
138
+ * generated slave config therefore says `0.0.0.0`. The same value then
139
+ * published two more surfaces — an authentication endpoint and a WebSocket
140
+ * one that has no rate limit available to it — and a slave serves no console,
141
+ * so nothing dials either. They were cost without benefit, on a machine whose
142
+ * whole point is being somewhere else.
143
+ */
144
+ export function consoleBindHostFor(dc) {
145
+ return dc.role === 'slave' ? '127.0.0.1' : resolveBindHost(dc.host);
146
+ }
147
+ /** Addresses that reach this host and nowhere else. */
148
+ export function isLoopbackHost(host) {
149
+ if (!host)
150
+ return true; // absent means the loopback default above
151
+ const h = host.trim().toLowerCase();
152
+ return h === '127.0.0.1' || h === 'localhost' || h === '::1' || h === '[::1]' || h.startsWith('127.');
153
+ }
154
+ /**
155
+ * Is the daemon still accepting the password its own migration seeded?
156
+ *
157
+ * Resolves the auth service lazily and answers `false` on any failure — a
158
+ * daemon that cannot reach its database has a louder problem than this, and
159
+ * a warning that fails the boot would be a worse trade than the risk it
160
+ * describes.
161
+ */
162
+ async function isSeededAdminPasswordInUse(app, logger) {
163
+ try {
164
+ const auth = await app.container.resolveAsync(AUTH_SERVICE_TOKEN);
165
+ return await auth.isUsingSeededAdminPassword();
166
+ }
167
+ catch (err) {
168
+ logger.warn({ err: err.message }, 'Could not check whether the seeded admin password is still in use');
169
+ return false;
170
+ }
171
+ }
172
+ /**
173
+ * The address other fleet nodes should dial to reach this one.
174
+ *
175
+ * `peer.address` is not decoration: `leader-election.ts` calls `requestVote`
176
+ * and `leaderHeartbeat` against it. A node bound to `0.0.0.0` therefore may
177
+ * not advertise `0.0.0.0` (not routable) — and must not advertise
178
+ * `127.0.0.1` either, which is what it did: every other node in the fleet
179
+ * resolves that to itself, so votes and heartbeats meant for the leader go
180
+ * to the sender's own loopback. A wrong answer of the right shape.
181
+ *
182
+ * When the bind address says nothing about reachability, take the first
183
+ * non-internal IPv4 the host has. Loopback remains the honest answer only
184
+ * when there is genuinely nothing else — a single-host deployment, where
185
+ * no other node can reach this one regardless.
186
+ */
187
+ export function advertisedAddress(bindHost) {
188
+ if (bindHost && bindHost !== '0.0.0.0' && bindHost !== '::')
189
+ return bindHost;
190
+ for (const addrs of Object.values(os.networkInterfaces())) {
191
+ for (const a of addrs ?? []) {
192
+ if (a.family === 'IPv4' && !a.internal)
193
+ return a.address;
194
+ }
195
+ }
196
+ return '127.0.0.1';
197
+ }
53
198
  export class OmnitronDaemon {
54
199
  app = null;
55
200
  pidManager = null;
201
+ /** Port this daemon serves `/netron/invoke` on; advertised to fleet peers. */
202
+ rpcHttpPort = null;
56
203
  fileWatcher = null;
57
204
  infraService = null;
205
+ eventLoopWatch = null;
58
206
  eventBroadcaster = null;
59
207
  syncService = null;
60
208
  nodeManagerService = null;
209
+ slaveConnector = null;
210
+ meshHandle = null;
61
211
  nodeManagerRpcService = null;
212
+ /**
213
+ * The live record of release builds this daemon is running.
214
+ *
215
+ * Held on the daemon rather than in the container because it is the state
216
+ * of work in progress: a build that a restart kills leaves a release
217
+ * directory with no manifest, and that — not this — is the durable trace.
218
+ */
219
+ releaseService = null;
62
220
  systemWorkerManager = null;
221
+ /** Everything the health-monitor worker needs to be spawned again. */
222
+ healthWorkerSpawn = null;
223
+ healthWorkerRestartTimer = null;
224
+ healthWorkerRestartAttempts = 0;
225
+ /** The startup budget the worker is given, so a failure can name it. */
226
+ healthWorkerStartupTimeout = 0;
227
+ /** In-daemon check timer, armed only while the worker is down. */
228
+ fallbackNodeCheckTimer = null;
229
+ /** Set by the shutdown task so nothing schedules work on the way out. */
230
+ isShuttingDown = false;
63
231
  leaderElection = null;
64
232
  configSyncService = null;
65
233
  metricsServer = null;
@@ -69,12 +237,17 @@ export class OmnitronDaemon {
69
237
  get daemonConfig() { return this.dc; }
70
238
  async start(config, options, daemonConfig) {
71
239
  this.dc = daemonConfig ?? DEFAULT_DAEMON_CONFIG;
72
- const pidFile = this.dc.pidFile.replace('~', process.env['HOME'] ?? '');
240
+ const pidFile = expandPath(this.dc.pidFile);
73
241
  this.pidManager = new PidManager(pidFile);
74
242
  if (this.pidManager.isRunning()) {
75
243
  const existingPid = this.pidManager.getPid();
76
244
  throw new Error(`Omnitron daemon already running (PID: ${existingPid})`);
77
245
  }
246
+ // Stale file from a previous crash is normal — clean it up so the
247
+ // atomic `O_EXCL` write below succeeds. P1-L: doing this AFTER
248
+ // isRunning() returned false means we only delete known-dead
249
+ // markers; live daemons get the conflict error above.
250
+ this.pidManager.remove();
78
251
  this.pidManager.write();
79
252
  // 1. Create Titan Application (the daemon itself)
80
253
  const DaemonModule = createDaemonModule(config, this.dc);
@@ -82,6 +255,25 @@ export class OmnitronDaemon {
82
255
  name: 'omnitron',
83
256
  version: CLI_VERSION,
84
257
  });
258
+ // P1-M — install our own SIGHUP handler BEFORE Application's
259
+ // default shutdown handlers attach, so `kill -HUP <daemon>`
260
+ // triggers a config reload (the Unix convention for long-running
261
+ // daemons) instead of terminating the supervisor. We don't bind
262
+ // this through the Application's signal manager because that
263
+ // path is wired to `runShutdownTasks()`; we install at the
264
+ // process level and the listener runs first.
265
+ process.on('SIGHUP', () => {
266
+ this.reloadConfigOnHup().catch((err) => {
267
+ // The message already carries which steps applied — printing only
268
+ // `.message` was fine for the load failure and misleading for the
269
+ // apply failure, which is the one that leaves work half-done.
270
+ console.warn(`[omnitron] SIGHUP config reload failed: ${err.message}`);
271
+ const cause = err.cause;
272
+ if (cause) {
273
+ console.warn(`[omnitron] caused by: ${cause.stack ?? String(cause)}`);
274
+ }
275
+ });
276
+ });
85
277
  // 2. Register transports (Unix, TCP, HTTP, WebSocket)
86
278
  await this.registerTransports(this.dc);
87
279
  // 3. Resolve and expose RPC services via Netron
@@ -90,6 +282,12 @@ export class OmnitronDaemon {
90
282
  await this.app.start();
91
283
  const loggerModule = await this.app.container.resolveAsync(LOGGER_SERVICE_TOKEN);
92
284
  const logger = loggerModule.logger;
285
+ // As soon as there is somewhere to say it, so a stall in the boot's own
286
+ // steps — infrastructure, migrations — is seen too. A node whose loop
287
+ // stands still answers nothing, its heartbeat included, and until this
288
+ // nothing on the node could say so. See `monitoring/event-loop-watch.ts`.
289
+ this.eventLoopWatch = new EventLoopWatch(logger);
290
+ this.eventLoopWatch.start();
93
291
  logger.info({
94
292
  socketPath: this.dc.socketPath,
95
293
  port: this.dc.port,
@@ -113,7 +311,7 @@ export class OmnitronDaemon {
113
311
  {
114
312
  const orchestrator = await this.app.container.resolveAsync(ORCHESTRATOR_TOKEN);
115
313
  if (this.app.netron) {
116
- const socketPath = this.dc.socketPath.replace('~', process.env['HOME'] ?? '');
314
+ const socketPath = expandPath(this.dc.socketPath);
117
315
  orchestrator.setDaemonNetron(this.app.netron, socketPath);
118
316
  }
119
317
  // 8.6. Wire metrics bridge + start /metrics HTTP endpoint.
@@ -148,7 +346,24 @@ export class OmnitronDaemon {
148
346
  if (savedConfig?.webapp) {
149
347
  try {
150
348
  const { WebappService } = await import('../webapp/webapp.service.js');
151
- const webapp = new WebappService(logger, process.cwd(), (this.dc.httpPort ?? 9800) + 1, this.dc.httpPort ?? 9800);
349
+ // Publishing the console while the seeded password is still in
350
+ // place hands the whole control plane — start, stop, restart, logs,
351
+ // config — to anyone who can reach the port and guess a word that
352
+ // is written in the migration. Migration 001 says the password
353
+ // "MUST be changed on first login"; nothing enforced it, and a
354
+ // comment is not a control.
355
+ //
356
+ // So the console falls back to loopback rather than refusing to
357
+ // start: the operator needs it to change the password, and a
358
+ // control plane that will not come up is its own outage. The log
359
+ // line says what to do and that this is not the configured bind.
360
+ let consoleHost = this.dc.consoleBindHost ?? '127.0.0.1';
361
+ if (!isLoopbackHost(consoleHost) && (await isSeededAdminPasswordInUse(this.app, logger))) {
362
+ logger.error({ configured: consoleHost, bound: '127.0.0.1' }, 'REFUSING to publish the Console while the seeded admin password is unchanged — ' +
363
+ 'binding loopback instead. Sign in at http://127.0.0.1 and change it, then restart.');
364
+ consoleHost = '127.0.0.1';
365
+ }
366
+ const webapp = new WebappService(logger, process.cwd(), (this.dc.httpPort ?? 9800) + 1, this.dc.httpPort ?? 9800, consoleHost);
152
367
  await webapp.start();
153
368
  }
154
369
  catch (err) {
@@ -156,14 +371,34 @@ export class OmnitronDaemon {
156
371
  }
157
372
  }
158
373
  }
374
+ // 8.9. Fleet monitoring. Started BEFORE managed apps and not awaited.
375
+ //
376
+ // It used to be step 11.5, after `startApps`, `startScheduledTasks` and
377
+ // `startBackgroundServices`. Fleet monitoring is about MACHINES; it has
378
+ // nothing to do with whether this daemon's managed applications start, and
379
+ // gating it behind them means one application that fails its startup
380
+ // leaves the whole fleet unwatched for as long as the supervisor keeps
381
+ // retrying it. Measured on this host: ten minutes after a daemon start,
382
+ // with two applications still cycling through failed starts, not a single
383
+ // node had been checked and nothing had said so.
384
+ //
385
+ // Not awaited, for the same reason in the other direction: spawning the
386
+ // worker has a 30-second startup timeout, and the applications should not
387
+ // wait behind it. The function handles and logs its own failures, and arms
388
+ // the in-daemon fallback checks when the worker cannot run.
389
+ void this.startHealthMonitorWorker(logger).catch((err) => {
390
+ // It handles its own failures; this is the last resort, because an
391
+ // unhandled rejection is a crash of the daemon rather than a lost
392
+ // subsystem.
393
+ logger.error({ error: err.message }, 'Health monitor startup threw');
394
+ });
159
395
  // 9. Start managed apps (waits for infra gate if apps have `requires`)
160
396
  await this.startApps(config, logger);
161
397
  // 10. Start background tasks via titan-scheduler (replaces bare setInterval)
162
398
  await this.startScheduledTasks(config, logger);
163
399
  // 11. Start remaining background services (telemetry, traces, leader election)
164
400
  await this.startBackgroundServices(config, logger);
165
- // 11.5. Start health monitor system worker (master only)
166
- await this.startHealthMonitorWorker(logger);
401
+ // (Fleet monitoring is started at 8.5, above — before managed apps.)
167
402
  // 12. Wire event broadcasting (orchestrator events → WebSocket push)
168
403
  await this.wireEventBroadcasting(logger);
169
404
  // 13. Register shutdown tasks — Application handles SIGTERM/SIGINT
@@ -186,18 +421,87 @@ export class OmnitronDaemon {
186
421
  this.app.netron.registerTransportServer('unix', {
187
422
  name: 'daemon-local',
188
423
  options: {
189
- path: dc.socketPath.replace('~', process.env['HOME'] ?? ''),
424
+ path: expandPath(dc.socketPath),
190
425
  force: true,
191
426
  mode: 0o600, // Owner-only access for security
427
+ // Every other party on this socket states a deadline and this one
428
+ // did not, so `netron.ts:797` — `serverRequestTimeout ??
429
+ // transportOpts.requestTimeout` — found both undefined and built its
430
+ // accepted-connection peers with netron's 5 s default, which is a
431
+ // deadline for a wire request rather than for management-plane work.
432
+ // The reading of the server option was added deliberately («a
433
+ // `requestTimeout` set on the server was silently ignored»); the
434
+ // value was never supplied. Same number as `CLI_REQUEST_TIMEOUT`,
435
+ // which the daemon's own client uses over this same socket.
436
+ requestTimeout: DAEMON_SOCKET_REQUEST_TIMEOUT,
437
+ // The same bridge the HTTP and WebSocket servers below have: it puts
438
+ // the context the guard admitted a call with — here the implicit
439
+ // `omnitron-local` set just after this — where the services read
440
+ // who is calling. This server had none, so for every CLI call the
441
+ // guard saw `omnitron-local` and the services saw nobody: the audit
442
+ // trail recorded `system` for 171 of the master's 178 rows, and an
443
+ // operator's deployment read exactly like the daemon's own boot
444
+ // autostart. No proxy stands in front of a unix socket, so the
445
+ // wrapper trusts no forwarded address — its default.
446
+ invocationWrapper: createAuthContextWrapper(),
192
447
  },
193
448
  });
194
- // TCP — remote fleet communication (cross-server)
449
+ // Local CLI trust. The Unix socket above is created mode 0600, so any
450
+ // peer that can open it is provably the same OS user that owns this
451
+ // daemon — the OS has already authenticated them. Grant those
452
+ // connections an implicit admin context so the local CLI satisfies the
453
+ // `@Public({ auth: { roles } })` guards on the daemon RPC services
454
+ // without a redundant JWT handshake (the CLI talks over this socket
455
+ // only). Remote transports — TCP (fleet), HTTP/WS (webapp) — are NOT
456
+ // registered here and keep requiring a validated token, so this widens
457
+ // nothing beyond same-user local access that the socket mode already
458
+ // grants.
459
+ this.app.netron.setTransportAuthContext('unix', {
460
+ userId: 'omnitron-local',
461
+ roles: [ROLES.ADMIN],
462
+ permissions: [],
463
+ metadata: { source: 'local-unix-socket' },
464
+ });
465
+ // TCP — remote fleet communication (cross-server). The default is
466
+ // 127.0.0.1 so a single-host deployment never exposes daemon control
467
+ // to the LAN; multi-host fleet operators opt in via `daemon.host`.
468
+ //
469
+ // The default belongs in DEFAULT_DAEMON_CONFIG, not here. It used to be
470
+ // enforced here instead, as `dc.host !== '0.0.0.0' ? dc.host :
471
+ // '127.0.0.1'` over a config default of `'0.0.0.0'` — absence encoded as
472
+ // a value from the domain of valid answers. `0.0.0.0` is not a marker for
473
+ // "unset", it is a bind address with a meaning, and it was the exact
474
+ // value the comment above it told fleet operators to set. Following the
475
+ // documentation produced loopback and no fleet, silently. It also
476
+ // contradicted every neighbouring knob: `consoleBindHost: '0.0.0.0'`
477
+ // publishes, and so does an app's transport host.
478
+ //
479
+ // (The original hazard — pre-fix this defaulted to all-interfaces while
480
+ // the AuthorizationManager below was missing, giving any LAN host an
481
+ // unauthenticated `stopAll({force:true})` — is answered by the default
482
+ // being loopback and by that manager now being wired.)
483
+ const tcpHost = fleetBindHostFor(dc);
484
+ if (!isLoopbackHost(tcpHost)) {
485
+ // Said, not enforced, and the difference is deliberate: silently
486
+ // rebinding a fleet transport to loopback would take a cluster down,
487
+ // and a node that cannot be reached is an outage rather than a
488
+ // hardening. The console above IS rebound, because its whole purpose
489
+ // is a password prompt and loopback still serves it.
490
+ void (async () => {
491
+ const loggerModule = await this.app.container.resolveAsync(LOGGER_SERVICE_TOKEN);
492
+ const log = loggerModule.logger;
493
+ if (await isSeededAdminPasswordInUse(this.app, log)) {
494
+ log.error({ host: tcpHost, port: dc.port }, 'The daemon fleet transport is published and the seeded admin password is unchanged — ' +
495
+ 'anyone who can reach this port can sign in and stop every app. Change it now.');
496
+ }
497
+ })().catch(() => undefined);
498
+ }
195
499
  this.app.netron.registerTransport('tcp', () => new TcpTransport());
196
500
  this.app.netron.registerTransportServer('tcp', {
197
501
  name: 'daemon-fleet',
198
502
  options: {
199
503
  port: dc.port,
200
- host: dc.host,
504
+ host: tcpHost,
201
505
  },
202
506
  });
203
507
  // Auth — JWT validation + session check + role extraction
@@ -246,18 +550,79 @@ export class OmnitronDaemon {
246
550
  },
247
551
  tokenCache: { enabled: true, ttl: 10_000 },
248
552
  });
249
- this.app.netron.configureAuth(authManager);
250
- const authContextWrapper = createAuthContextWrapper();
553
+ // Wire the AuthorizationManager so the `@Public({ auth: { roles } })`
554
+ // decorators on DaemonRpcService are actually enforced on the wire.
555
+ // Without an authzManager, `remote-peer.ts:enforceMethodAccess`
556
+ // early-returns and every method is reachable by anyone who can
557
+ // mint a valid JWT, regardless of `roles`. The WS origin policy
558
+ // (`allowedOrigins: true` → same-host) closes the cross-origin
559
+ // upgrade attack against cookie-mode auth.
560
+ const authzManager = new AuthorizationManager(authLogger);
561
+ this.app.netron.configureAuth(authManager, authzManager, {
562
+ allowedOrigins: true,
563
+ });
564
+ // `trustProxy` is shared with the rate limiter deliberately: both answer
565
+ // the same question — is there a proxy in front whose forwarded headers
566
+ // we may believe? Two separate switches would eventually disagree.
567
+ const authContextWrapper = createAuthContextWrapper({
568
+ trustProxy: dc.httpRateLimit?.trustProxy === true,
569
+ });
570
+ // HTTP / WS bind also defaults to 127.0.0.1. The webapp talks to
571
+ // these over the same loopback; nginx fronts the public surface.
572
+ // Operators who proxy in from another host opt in via `daemon.host`
573
+ // (same knob that controls TCP, with the same literal meaning).
574
+ //
575
+ // Except on a slave, where that knob has to be published for a different
576
+ // reason and publishing these with it is pure cost. A fleet node must
577
+ // bind its TCP transport somewhere the master can reach — that is what
578
+ // `daemon.host: '0.0.0.0'` in a generated slave config is for — and the
579
+ // same value then published the Netron HTTP RPC and the WebSocket
580
+ // alongside it. A slave serves no console: the operator uses the
581
+ // master's. So the two surfaces it gained are an authentication endpoint
582
+ // it does not need and a WebSocket one that, as the note below records,
583
+ // has no rate limit available to it.
584
+ //
585
+ // Rebound rather than warned about, which is the opposite of the choice
586
+ // made for the TCP transport twenty lines up — and for the opposite
587
+ // reason. Rebinding the fleet port would take a cluster down, because
588
+ // being reachable is that port's whole job. Rebinding these costs a slave
589
+ // nothing, because nothing dials them.
590
+ const localHost = consoleBindHostFor(dc);
251
591
  // HTTP — Netron RPC API (internal port, nginx proxies from public port)
592
+ // The public `httpPort` (9800) is nginx in front of the console; the
593
+ // daemon's own Netron HTTP listener sits one above it. Remembered here
594
+ // because the fleet registration below must advertise the port peers can
595
+ // actually POST `/netron/invoke` to — not the TCP port, and not the
596
+ // console's.
252
597
  const internalHttpPort = (dc.httpPort ?? 9800) + 1;
598
+ this.rpcHttpPort = internalHttpPort;
253
599
  this.app.netron.registerTransport('http', () => new HttpTransport());
600
+ const rl = dc.httpRateLimit ?? {};
254
601
  this.app.netron.registerTransportServer('http', {
255
602
  name: 'daemon-http',
256
603
  options: {
257
604
  port: internalHttpPort,
258
- host: '0.0.0.0',
605
+ host: localHost,
259
606
  cors: true,
260
607
  invocationWrapper: authContextWrapper,
608
+ // Volume cap on the HTTP RPC surface, and only that one. Password
609
+ // guessing is bounded by the per-account lockout in AuthService.
610
+ //
611
+ // The WebSocket surface below is NOT covered: netron's WebSocket
612
+ // transport has no rate-limit option — `maxPayload` is the only
613
+ // bound it accepts — and the console talks over WebSocket. This
614
+ // comment used to say the limit bounded "everything else, including
615
+ // attempts to flood the daemon into unresponsiveness", which
616
+ // describes a protection that covers one of the two transports an
617
+ // attacker can reach.
618
+ rateLimit: {
619
+ enabled: rl.enabled ?? true,
620
+ windowMs: rl.windowMs ?? 60_000,
621
+ maxRequests: rl.maxRequests ?? 3_000,
622
+ globalMaxRequests: rl.globalMaxRequests ?? 6_000,
623
+ trustProxy: rl.trustProxy ?? false,
624
+ ...(rl.whitelist ? { whitelist: rl.whitelist } : {}),
625
+ },
261
626
  },
262
627
  });
263
628
  // WebSocket — real-time event push to webapp clients
@@ -267,8 +632,18 @@ export class OmnitronDaemon {
267
632
  name: 'daemon-ws',
268
633
  options: {
269
634
  port: wsPort,
270
- host: '0.0.0.0',
635
+ host: localHost,
636
+ // Applied per invocation by `remote-peer.ts`, the dispatcher every
637
+ // socket transport shares — so WS calls get the same request context
638
+ // HTTP calls do. Until recently only the HTTP server read this
639
+ // option and this line did nothing.
271
640
  invocationWrapper: authContextWrapper,
641
+ // The only bound this transport accepts. Left unset it inherits
642
+ // `ws`'s 100 MB default, which is a lot of memory to hand a single
643
+ // frame on a control-plane socket that carries RPC arguments and
644
+ // event pushes. 8 MB is well above anything the console sends and
645
+ // well below anything worth allocating for a stranger.
646
+ maxPayload: dc.wsMaxPayload ?? 8 * 1024 * 1024,
272
647
  },
273
648
  });
274
649
  }
@@ -286,11 +661,20 @@ export class OmnitronDaemon {
286
661
  const logManager = await container.resolveAsync(LOG_MANAGER_TOKEN);
287
662
  const logCollector = await container.resolveAsync(LOG_COLLECTOR_TOKEN);
288
663
  const loggerModule = await container.resolveAsync(LOGGER_SERVICE_TOKEN);
664
+ // Table retention. `logging.maxSize` and `maxFiles` bound the rotated
665
+ // files on disk and say nothing about the `logs` table, which had no
666
+ // bound at all — 13 GB on the development host. Off unless configured,
667
+ // and the number comes from the config rather than a default chosen
668
+ // inside the collector, because a wrong default there deletes history.
669
+ logCollector.setRetentionDays(config.logging?.databaseRetentionDays ?? 0, loggerModule.logger.child({ component: 'log-retention' }));
289
670
  const rpcService = new DaemonRpcService(orchestrator, titanHealth, logManager, config, this);
290
671
  await this.app.netron.peer.exposeService(rpcService);
291
672
  // Logs RPC service (works on both master and slave — uses PG or SQLite)
292
673
  const logsRpcService = new LogsRpcService(logCollector);
293
674
  await this.app.netron.peer.exposeService(logsRpcService);
675
+ // `getLogs` answers an app this daemon does not run from the stored rows —
676
+ // where a node's synced logs are.
677
+ rpcService.setLogStore(logCollector);
294
678
  // Metrics RPC service — provided by TitanMetricsModule
295
679
  const metricsService = await container.resolveAsync(TITAN_METRICS_TOKEN);
296
680
  const { MetricsRpcService: MetricsRpc } = await import('@omnitron-dev/titan-metrics');
@@ -301,10 +685,29 @@ export class OmnitronDaemon {
301
685
  const healthCheckService = await container.resolveAsync(HEALTH_CHECK_SERVICE_TOKEN);
302
686
  const healthCheckRpcService = new HealthCheckRpc(healthCheckService);
303
687
  await this.app.netron.peer.exposeService(healthCheckRpcService);
688
+ // The audit trail. Master-only — its table lives in the omnitron
689
+ // database — and every service that records into it takes it as an
690
+ // optional dependency, so a slave serves the same methods and records
691
+ // nothing rather than refusing them.
692
+ let audit;
693
+ if (!isSlave) {
694
+ try {
695
+ audit = await container.resolveAsync(AUDIT_SERVICE_TOKEN);
696
+ // Revealing an app's secrets is recorded (`revealEnv`).
697
+ rpcService.setAudit(audit);
698
+ const { AuditRpcService } = await import('../services/audit.rpc-service.js');
699
+ await this.app.netron.peer.exposeService(new AuditRpcService(audit));
700
+ }
701
+ catch (err) {
702
+ // A daemon whose database did not come up still runs; it simply
703
+ // records nothing, and says so once rather than at every action.
704
+ loggerModule.logger.warn({ error: err.message }, 'No audit trail on this daemon — actions will not be recorded');
705
+ }
706
+ }
304
707
  // Secrets RPC service (file-based, no DB dependency)
305
708
  const { SecretsRpcService: SecretsRpc } = await import('../services/secrets.rpc-service.js');
306
709
  const secretsService = await container.resolveAsync(SECRETS_SERVICE_TOKEN);
307
- const secretsRpcService = new SecretsRpc(secretsService);
710
+ const secretsRpcService = new SecretsRpc(secretsService, audit);
308
711
  await this.app.netron.peer.exposeService(secretsRpcService);
309
712
  // Kubernetes RPC service (no DB dependency)
310
713
  const { KubernetesRpcService: K8sRpc } = await import('../services/kubernetes.rpc-service.js');
@@ -316,14 +719,60 @@ export class OmnitronDaemon {
316
719
  const backupService = await container.resolveAsync(BACKUP_SERVICE_TOKEN);
317
720
  const backupRpcService = new BackupRpc(backupService);
318
721
  await this.app.netron.peer.exposeService(backupRpcService);
722
+ // Re-arm any persisted backup schedules so they survive daemon restarts.
723
+ await backupService.restoreSchedules().catch((err) => loggerModule.logger.warn({ err: err.message }, 'Failed to restore backup schedules'));
319
724
  // Infrastructure RPC service (reports infra state — slave has no infra)
320
725
  const { InfrastructureRpcService: InfraRpc } = await import('../services/infrastructure.rpc-service.js');
321
- const infraRpcService = new InfraRpc(() => this.infraService);
726
+ const infraRpcService = new InfraRpc(() => this.infraService,
727
+ // Only a node hosts a stack's infrastructure on demand. A master
728
+ // provisions its own from its own config at boot, and handing it a
729
+ // second one over RPC would give one machine two reconcilers over one
730
+ // set of containers.
731
+ isSlave
732
+ ? (infraConfig, declaredServices, presetRegistry, serviceOverrides) => {
733
+ const service = new InfrastructureService(loggerModule.logger.child({ component: 'infra' }), infraConfig, declaredServices, presetRegistry, serviceOverrides,
734
+ // A node keeps its own state in SQLite. Provisioning a
735
+ // control-plane Postgres for it gives the host a database
736
+ // nobody queries, on default credentials.
737
+ false);
738
+ this.infraService = service;
739
+ return service;
740
+ }
741
+ : undefined,
742
+ // The node's own vault. Generated service credentials are written
743
+ // here and read back on every later provision, which is what makes a
744
+ // generated password usable: a data directory keeps the one it was
745
+ // initialised with.
746
+ // Resolved per call, not captured here: a resolution that fails during
747
+ // startup would otherwise leave credential generation off for the life
748
+ // of this daemon, and the absence reads as "this node needs none".
749
+ isSlave
750
+ ? () => {
751
+ try {
752
+ return container.resolve(SECRETS_SERVICE_TOKEN);
753
+ }
754
+ catch {
755
+ return undefined;
756
+ }
757
+ }
758
+ : undefined,
759
+ // The logger, because the one thing this service says on its own — that
760
+ // a deployment is running on a default credential — is said at error
761
+ // level and reaches nobody without it.
762
+ loggerModule.logger.child({ component: 'infra' }));
322
763
  await this.app.netron.peer.exposeService(infraRpcService);
323
764
  // Project + Stack management RPC service
324
765
  const projectService = await container.resolveAsync(PROJECT_SERVICE_TOKEN);
325
- const projectRpcService = new ProjectRpcService(projectService);
766
+ const projectRpcService = new ProjectRpcService(projectService, audit);
326
767
  await this.app.netron.peer.exposeService(projectRpcService);
768
+ // Releases. Master-only, like the builds themselves: a release is built
769
+ // from clean clones of BOTH repositories, and only a master has both.
770
+ if (!isSlave) {
771
+ const { ReleaseService } = await import('../services/release.service.js');
772
+ const { ReleaseRpcService } = await import('../services/release.rpc-service.js');
773
+ this.releaseService = new ReleaseService(loggerModule.logger.child({ component: 'release' }));
774
+ await this.app.netron.peer.exposeService(new ReleaseRpcService(this.releaseService, audit, projectService));
775
+ }
327
776
  // Sync service (slave→master data replication)
328
777
  const syncNodeId = `${os.hostname()}-${dc.port}`;
329
778
  let syncDb;
@@ -335,6 +784,53 @@ export class OmnitronDaemon {
335
784
  syncDb = await container.resolveAsync(OMNITRON_DB_TOKEN);
336
785
  }
337
786
  this.syncService = new SyncService(syncDb, loggerModule.logger.child({ component: 'sync' }), syncNodeId, dc.role, dc.sync);
787
+ // Remote metrics into the store the console actually reads.
788
+ //
789
+ // `ingestMetric` wrote them to `metrics_raw`, and nothing in this
790
+ // repository reads that table — the metrics page queries titan-metrics'
791
+ // own storage. So once the pipeline carried anything, it would have
792
+ // carried it somewhere invisible. Recording through the same service the
793
+ // master uses for its own readings makes a remote node a LABEL on the
794
+ // existing series instead of a second store of the same concept.
795
+ //
796
+ // `node` is a distinct argument in the sink's signature rather than a
797
+ // key a caller may forget to put in `labels`: a sample recorded without
798
+ // it merges with the master's own, and the chart then shows two machines
799
+ // summed under one name.
800
+ if (!isSlave) {
801
+ const syncMetrics = await container.resolveAsync(TITAN_METRICS_TOKEN);
802
+ this.syncService.setMetricsSink((sample) => {
803
+ try {
804
+ syncMetrics.recordTyped('gauge', sample.name, { ...sample.labels, app: sample.app, node: sample.node }, sample.value);
805
+ }
806
+ catch (err) {
807
+ loggerModule.logger.warn({ node: sample.node, name: sample.name, error: err.message }, 'Failed to record a replicated metric');
808
+ }
809
+ });
810
+ }
811
+ // The producer the replication pipeline never had.
812
+ //
813
+ // `SyncService` buffers, batches, retries with backoff, evicts when over
814
+ // budget, and the master keeps a dedup ledger for what it has taken —
815
+ // all of it built, and `buffer`/`bufferBatch` had ZERO callers. A slave
816
+ // collected its logs into its own SQLite and replicated none of them,
817
+ // and nothing said so: an empty buffer drains successfully every cycle.
818
+ //
819
+ // Wired only for a slave. `bufferBatch` is a no-op for any other role,
820
+ // so this is belt and braces rather than the guard itself — but a master
821
+ // has no master to ship to, and doing the work would be pure cost.
822
+ if (isSlave) {
823
+ const syncService = this.syncService;
824
+ logCollector.setSyncSink((entries) => {
825
+ void syncService
826
+ .bufferBatch(entries.map((e) => ({ category: 'logs', payload: e })))
827
+ .catch(() => {
828
+ // `bufferBatch` logs its own failure. Swallowed here because the
829
+ // lines are already in the local table: losing the replica copy
830
+ // must not cost the flush that succeeded.
831
+ });
832
+ });
833
+ }
338
834
  const syncRpcService = new SyncRpcService(this.syncService);
339
835
  await this.app.netron.peer.exposeService(syncRpcService);
340
836
  // System info service (real-time OS/hardware metrics)
@@ -343,19 +839,120 @@ export class OmnitronDaemon {
343
839
  await this.app.netron.peer.exposeService(systemInfoRpcService);
344
840
  // Event Broadcaster RPC service (WebSocket subscriptions)
345
841
  this.eventBroadcaster = new EventBroadcasterService(loggerModule.logger);
346
- const eventBroadcasterRpcService = new EventBroadcasterRpcService(this.eventBroadcaster);
842
+ const eventBroadcasterRpcService = new EventBroadcasterRpcService(this.eventBroadcaster, this.app.netron);
347
843
  await this.app.netron.peer.exposeService(eventBroadcasterRpcService);
348
844
  // Node manager — file-based, no PG (master only — manages infrastructure nodes)
349
845
  // Health checks delegated to dedicated system worker (started later after infra is ready)
350
846
  if (!isSlave) {
351
847
  const { NodeManagerService } = await import('../services/node-manager.service.js');
352
848
  const { NodeManagerRpcService } = await import('../services/node-manager.rpc-service.js');
353
- const secretsService = await container.resolveAsync(SECRETS_SERVICE_TOKEN);
354
- const nodeManager = new NodeManagerService(loggerModule.logger.child({ component: 'nodes' }), secretsService);
849
+ const { DaemonStateStore: DaemonStateStoreCls } = await import('./daemon-state-store.service.js');
850
+ const daemonStateStore = await container.resolveAsync(DAEMON_STATE_STORE_TOKEN);
851
+ const nodeManager = new NodeManagerService(loggerModule.logger.child({ component: 'nodes' }), daemonStateStore, secretsService);
355
852
  this.nodeManagerService = nodeManager;
356
- const nodeManagerRpcService = new NodeManagerRpcService(nodeManager);
853
+ const nodeManagerRpcService = new NodeManagerRpcService(nodeManager, audit);
854
+ // The deployer reaches nodes through the same SSH implementation as the
855
+ // health checks — the only one that can present a stored password or a
856
+ // key passphrase.
857
+ {
858
+ const { RemoteDeployer } = await import('../services/remote-deployer.service.js');
859
+ const { ExecutionService } = await import('../execution/execution.service.js');
860
+ const deployLogger = loggerModule.logger.child({ component: 'deploy' });
861
+ const remoteDeployer = new RemoteDeployer(deployLogger, new ExecutionService(deployLogger));
862
+ nodeManagerRpcService.setRemoteDeployer(remoteDeployer);
863
+ // Upgrading a node from the console: the same build the CLI makes,
864
+ // made here, then the same two calls. Without this the console could
865
+ // see every node and update none of them.
866
+ const { NodeUpgradeService } = await import('../services/node-upgrade.service.js');
867
+ nodeManagerRpcService.setUpgradeService(new NodeUpgradeService(loggerModule.logger.child({ component: 'node-upgrade' }), {
868
+ getNode: (id) => nodeManager.getNode(id),
869
+ // The same projection the CLI builds for `fleet upgrade`, made
870
+ // here so the console's plan and the terminal's dry run decide
871
+ // from identical inputs. Two places computing «what is this
872
+ // node running» is how they come to disagree.
873
+ listCandidates: () => nodeManager.listNodes().map((n) => ({
874
+ nodeId: n.id,
875
+ name: n.name,
876
+ currentVersion: n.status?.omnitronVersion ?? null,
877
+ isLocal: n.isLocal,
878
+ // SSH, not the daemon port: an upgrade travels over SSH,
879
+ // and a node whose daemon is unreachable is often exactly
880
+ // the one worth upgrading.
881
+ sshReachable: n.status?.sshConnected ?? null,
882
+ // The machine, for telling two registry names for one box
883
+ // apart before both get the bundle.
884
+ address: `${n.host}:${n.sshPort}`,
885
+ })),
886
+ }, (id) => nodeManager.nodeToDeployTarget(id), () => remoteDeployer, audit));
887
+ }
357
888
  this.nodeManagerRpcService = nodeManagerRpcService;
889
+ // The local node's own answers, for the three readers that ask every
890
+ // node about itself. A daemon has no mesh connection to itself, so
891
+ // `askNode` needs a local source for each.
892
+ //
893
+ // On the INSTANCE, after it is assigned, and not on `this.…?.` from
894
+ // wherever each dependency happens to be constructed: `setSyncService`
895
+ // was first written next to `new SyncService(...)`, ninety lines above
896
+ // this assignment and inside the same function, where `?.` swallowed it
897
+ // silently and the local node would have reported "not wired" forever.
898
+ // An optional call is only safe where the object is known to exist.
899
+ nodeManagerRpcService.setSyncService(this.syncService);
358
900
  await this.app.netron.peer.exposeService(nodeManagerRpcService);
901
+ // The mesh: every registered node, connected and replicating.
902
+ //
903
+ // `SlaveConnector` maintains the connections and pulls each node's
904
+ // write-ahead buffer on connect and on every heartbeat, and all of it
905
+ // worked — but the only caller of `addSlave` was a remote or cluster
906
+ // stack starting. So the master connected to the nodes something had
907
+ // been deployed onto, and to no others. A node added through the
908
+ // console, provisioned, and collecting its own metrics and logs was
909
+ // never dialled, and buffered locally forever: measured at 47,407
910
+ // undelivered entries over eleven hours on the first such node, which
911
+ // reported healthy the whole time.
912
+ //
913
+ // Built here rather than inside ProjectService because reaching a node
914
+ // needs what only this side has — the registry's SSH credentials, for
915
+ // the token the node's daemon demands and for the tunnel a firewalled
916
+ // daemon port needs. ProjectService adopts the same instance: one per
917
+ // master, or two connectors pull the same buffer and race to ack it.
918
+ try {
919
+ const { SlaveConnector } = await import('../cluster/slave-connector.js');
920
+ const { createMeshDialer } = await import('../cluster/mesh-link.js');
921
+ const { startMesh } = await import('../cluster/mesh.js');
922
+ const { ExecutionService: Exec } = await import('../execution/execution.service.js');
923
+ const meshLogger = loggerModule.logger.child({ component: 'mesh' });
924
+ const connector = new SlaveConnector(meshLogger, this.syncService ?? null, {
925
+ dial: createMeshDialer({
926
+ logger: meshLogger,
927
+ execution: new Exec(meshLogger),
928
+ subject: `mesh:${os.hostname()}`,
929
+ sshTargetFor: async ({ host }) => {
930
+ const node = nodeManager.listNodes().find((n) => n.host === host && !n.isLocal);
931
+ return node ? nodeManager.nodeToSshTarget(node) : null;
932
+ },
933
+ }),
934
+ });
935
+ this.slaveConnector = connector;
936
+ projectService.setSlaveConnector(connector);
937
+ // So the console can answer "is this node replicating", which is a
938
+ // different question from "can this master reach it".
939
+ nodeManagerRpcService.setSlaveConnector(connector);
940
+ // A stack names a host; the registry holds the credential for it.
941
+ // Wired here because this is the side that has both — ProjectService
942
+ // reads config files, the node registry keeps its secrets in the
943
+ // daemon's vault.
944
+ projectService.setNodeCredentialResolver(async (host) => {
945
+ const node = nodeManager.listNodes().find((n) => n.host === host && !n.isLocal);
946
+ return node ? nodeManager.nodeToSshTarget(node) : null;
947
+ });
948
+ this.meshHandle = startMesh({ registry: nodeManager, connector, logger: meshLogger });
949
+ }
950
+ catch (err) {
951
+ // A master with no mesh still supervises its own apps, so this does
952
+ // not stop the daemon — but nothing else reports it, and a silent
953
+ // failure here looks exactly like a fleet that has nothing to say.
954
+ loggerModule.logger.error({ error: err.message }, 'Mesh not started — registered nodes will buffer their data locally and replicate nothing');
955
+ }
359
956
  }
360
957
  // =====================================================================
361
958
  // Master-only RPC services (require PostgreSQL)
@@ -374,6 +971,9 @@ export class OmnitronDaemon {
374
971
  const { TelemetryRpcService: TelemetryRpc } = await import('../services/telemetry.rpc-service.js');
375
972
  const telemetryRelay = await container.resolveAsync(TELEMETRY_RELAY_TOKEN);
376
973
  const telemetryRpcService = new TelemetryRpc(telemetryRelay);
974
+ // The console asks every node for its relay, and a daemon has no mesh
975
+ // connection to itself, so the local node's answer comes from here.
976
+ this.nodeManagerRpcService?.setTelemetryRelay(telemetryRelay);
377
977
  await this.app.netron.peer.exposeService(telemetryRpcService);
378
978
  // Fleet management RPC service
379
979
  const { FleetRpcService: FleetRpc } = await import('../services/fleet.rpc-service.js');
@@ -413,6 +1013,8 @@ export class OmnitronDaemon {
413
1013
  ...(dc.cluster.heartbeatInterval != null && { heartbeatInterval: dc.cluster.heartbeatInterval }),
414
1014
  });
415
1015
  const clusterRpcService = new ClusterRpc(this.leaderElection);
1016
+ // The local node's own election state, for the cross-node reader.
1017
+ this.nodeManagerRpcService?.setLeaderElection(this.leaderElection);
416
1018
  await this.app.netron.peer.exposeService(clusterRpcService);
417
1019
  }
418
1020
  }
@@ -448,6 +1050,10 @@ export class OmnitronDaemon {
448
1050
  if (!this.app)
449
1051
  return;
450
1052
  const stateStore = await this.app.container.resolveAsync(STATE_STORE_TOKEN);
1053
+ // First-touch init reads from SQLite (and migrates the legacy
1054
+ // JSON snapshot in-place if one exists). Subsequent reads via
1055
+ // `load()` are synchronous from the in-memory cache.
1056
+ await stateStore.init();
451
1057
  const previousState = stateStore.load();
452
1058
  if (previousState) {
453
1059
  const runningApps = previousState.apps.filter((a) => a.status === 'online' || a.status === 'starting');
@@ -531,7 +1137,7 @@ export class OmnitronDaemon {
531
1137
  // stack auto-detection upstream); the daemon does not provision it
532
1138
  // again here.
533
1139
  const { ProjectRegistry } = await import('../project/registry.js');
534
- const projects = new ProjectRegistry().list();
1140
+ const projects = ProjectRegistry.open().list();
535
1141
  const projectModeActive = projects.some((p) => Array.isArray(p.enabledStacks) && p.enabledStacks.length > 0);
536
1142
  if (projectModeActive) {
537
1143
  logger.info({ projects: projects.length }, 'project mode active — daemon-level infrastructure deferred to stack provision');
@@ -590,27 +1196,70 @@ export class OmnitronDaemon {
590
1196
  */
591
1197
  async reconcileOrphanContainers(logger, projects, projectService) {
592
1198
  const { listManagedContainers, stopContainer, removeContainer } = await import('../infrastructure/container-runtime.js');
1199
+ const { decideOrphans } = await import('../infrastructure/orphan-containers.js');
593
1200
  const managed = await listManagedContainers();
594
1201
  if (managed.length === 0)
595
1202
  return;
596
1203
  // Build set of expected container name prefixes from registered projects/stacks
597
1204
  const expectedPrefixes = new Set();
598
1205
  const internalNames = new Set(['omnitron-pg', 'omnitron-nginx']);
1206
+ // Load each project's config FIRST. `listStacks` reads the loaded-config
1207
+ // cache and returns [] for a project whose config has not been read —
1208
+ // and this runs before the per-project load loop further down, whose own
1209
+ // comment says loading "populates internal cache used by listStacks".
1210
+ //
1211
+ // So on every boot where projects come from the registry rather than CWD
1212
+ // auto-detection — the normal case — `expectedPrefixes` was EMPTY and
1213
+ // every managed container looked like an orphan. Measured from this
1214
+ // daemon's own log, 2026-09-12 12:46, ten RUNNING containers removed in
1215
+ // fourteen seconds: postgres, redis, bitcoin, monero (daemon and wallet),
1216
+ // nominatim, tor, tiles, minio, gateway — each logged as "not part of any
1217
+ // registered stack" while being part of the registered stack.
1218
+ //
1219
+ // Named volumes survived (`docker rm -f`, no `-v`), so this cost a cold
1220
+ // restart of every service rather than the chain data. That is luck, not
1221
+ // design.
1222
+ let expectationsComplete = true;
599
1223
  for (const project of projects) {
1224
+ try {
1225
+ await projectService.loadProjectConfig(project.name);
1226
+ }
1227
+ catch (err) {
1228
+ expectationsComplete = false;
1229
+ logger.warn({ project: project.name, error: err.message }, 'Could not read this project’s stacks — skipping orphan reconciliation rather than guessing');
1230
+ continue;
1231
+ }
600
1232
  const stacks = projectService.listStacks(project.name);
1233
+ if (stacks.length === 0)
1234
+ expectationsComplete = false;
601
1235
  for (const stack of stacks) {
602
1236
  expectedPrefixes.add(`${project.name}-${stack.name}-`);
603
1237
  }
604
1238
  }
1239
+ // Whether anything may be removed is one decision with several ways of
1240
+ // being wrong, and every way it has been wrong so far was a mistake about
1241
+ // what the inputs MEANT. It lives in `decideOrphans`, where it can be
1242
+ // pinned in a test rather than discovered on a host.
1243
+ const decision = decideOrphans({
1244
+ isSlave: this.dc?.role === 'slave',
1245
+ managed: managed.map((c) => ({
1246
+ name: c.name,
1247
+ status: c.status,
1248
+ project: c.project,
1249
+ stack: c.stack,
1250
+ })),
1251
+ projects: projects.map((p) => p.name),
1252
+ expectedPrefixes: [...expectedPrefixes],
1253
+ expectationsComplete,
1254
+ internalNames: [...internalNames],
1255
+ });
1256
+ if (decision.action === 'skip') {
1257
+ logger.warn({ managed: managed.length, expectedPrefixes: expectedPrefixes.size, because: decision.because }, 'Orphan reconciliation skipped');
1258
+ return;
1259
+ }
605
1260
  let orphanCount = 0;
606
- for (const container of managed) {
607
- // Always keep internal omnitron containers
608
- if (internalNames.has(container.name))
609
- continue;
610
- // Keep containers that match a known project/stack prefix
611
- if ([...expectedPrefixes].some((prefix) => container.name.startsWith(prefix)))
612
- continue;
613
- // This container is an orphan — stop and remove it
1261
+ for (const name of decision.containers) {
1262
+ const container = managed.find((c) => c.name === name);
614
1263
  logger.info({ container: container.name, status: container.status }, 'Removing orphan container (not part of any registered stack)');
615
1264
  try {
616
1265
  if (container.status === 'running')
@@ -626,49 +1275,23 @@ export class OmnitronDaemon {
626
1275
  logger.info({ removed: orphanCount }, 'Orphan container cleanup complete');
627
1276
  }
628
1277
  }
1278
+ /**
1279
+ * Apply the internal-database schema.
1280
+ *
1281
+ * Delegates to the shared runner (`database/migration-runner.ts`) so this
1282
+ * path, the infrastructure service and the `migrate` CLI all execute the
1283
+ * same registry through the same engine. A failure here is logged as an
1284
+ * ERROR — the daemon keeps running, but every feature backed by
1285
+ * omnitron-pg (alerts, sessions, logs, metrics, deployments) is degraded
1286
+ * and the operator has to see it.
1287
+ */
629
1288
  async runOmnitronMigrations(logger) {
1289
+ const { runOmnitronMigrations } = await import('../database/migration-runner.js');
630
1290
  try {
631
- const { Kysely, PostgresDialect, Migrator } = await import('kysely');
632
- const pg = await import('pg');
633
- const m001 = await import('../database/migrations/001_initial_schema.js');
634
- const m002 = await import('../database/migrations/002_metrics_raw.js');
635
- const m003 = await import('../database/migrations/003_pipelines_traces.js');
636
- const m004 = await import('../database/migrations/004_sync_buffer.js');
637
- const m005 = await import('../database/migrations/005_node_health_checks.js');
638
- const pool = new pg.default.Pool({
639
- host: 'localhost', port: 5480,
640
- database: 'omnitron', user: 'omnitron', password: 'omnitron',
641
- });
642
- const db = new Kysely({ dialect: new PostgresDialect({ pool }) });
643
- try {
644
- const migrator = new Migrator({
645
- db,
646
- provider: {
647
- async getMigrations() {
648
- return {
649
- '001_initial_schema': { up: m001.up, down: m001.down },
650
- '002_metrics_raw': { up: m002.up, down: m002.down },
651
- '003_pipelines_traces': { up: m003.up, down: m003.down },
652
- '004_sync_buffer': { up: m004.up, down: m004.down },
653
- '005_node_health_checks': { up: m005.up, down: m005.down },
654
- };
655
- },
656
- },
657
- });
658
- const { results, error } = await migrator.migrateToLatest();
659
- const applied = results?.filter((r) => r.status === 'Success') ?? [];
660
- if (applied.length > 0) {
661
- logger.info({ migrations: applied.map((r) => r.migrationName) }, `Applied ${applied.length} migration(s)`);
662
- }
663
- if (error)
664
- logger.warn({ error: String(error) }, 'Migration warning');
665
- }
666
- finally {
667
- await db.destroy();
668
- }
1291
+ await runOmnitronMigrations(logger);
669
1292
  }
670
1293
  catch (err) {
671
- logger.warn({ error: err.message }, 'Could not run migrations — will retry on next start');
1294
+ logger.error({ error: err.message }, 'Omnitron database migrations FAILED — alerts, sessions, logs and metrics will not work until this is resolved');
672
1295
  }
673
1296
  }
674
1297
  // ============================================================================
@@ -684,6 +1307,15 @@ export class OmnitronDaemon {
684
1307
  titanHealth.registerIndicator(new DockerHealthIndicator(() => this.infraService));
685
1308
  // Managed apps health indicator
686
1309
  titanHealth.registerIndicator(new AppHealthIndicator(orchestrator));
1310
+ // How long the loop has stood still, and when — the number the console
1311
+ // reads for a node that went quiet.
1312
+ if (this.eventLoopWatch)
1313
+ titanHealth.registerIndicator(new EventLoopStallIndicator(this.eventLoopWatch));
1314
+ // The console asks every node for its own indicators, and a daemon has
1315
+ // no mesh connection to itself — so the local node's answer comes from
1316
+ // here. Registered beside the indicators rather than in the wiring
1317
+ // above, because this is the object that has just been given them.
1318
+ this.nodeManagerRpcService?.setTitanHealth(titanHealth);
687
1319
  }
688
1320
  catch {
689
1321
  // Health module may not be available — non-critical
@@ -743,6 +1375,31 @@ export class OmnitronDaemon {
743
1375
  logger.info('No projects registered. Use `omnitron project add <name> <path>` or run from a directory with omnitron.config.ts');
744
1376
  return;
745
1377
  }
1378
+ // When there are enabled stacks to resume, wait for Docker first. Under
1379
+ // OS-service supervision the daemon boots at login, seconds before
1380
+ // OrbStack/dockerd finishes starting — on 2026-07-11 the one-shot resume
1381
+ // fired into "Docker is not available" and the platform stayed appless
1382
+ // for days. Bounded wait; on timeout we proceed and the enabled-stacks
1383
+ // reconciler keeps retrying with backoff.
1384
+ const hasEnabledStacks = allProjects.some((p) => (p.enabledStacks?.length ?? 0) > 0);
1385
+ if (hasEnabledStacks) {
1386
+ const { isDockerAvailable } = await import('../infrastructure/container-runtime.js');
1387
+ const deadline = Date.now() + 120_000;
1388
+ let waited = false;
1389
+ while (!(await isDockerAvailable())) {
1390
+ if (Date.now() > deadline) {
1391
+ logger.warn('Docker still unavailable after 120s — proceeding; reconciler will retry enabled stacks');
1392
+ break;
1393
+ }
1394
+ if (!waited) {
1395
+ logger.info('Docker not available yet (host still booting?) — waiting before stack resume');
1396
+ waited = true;
1397
+ }
1398
+ await new Promise((r) => setTimeout(r, 3_000));
1399
+ }
1400
+ if (waited)
1401
+ logger.info('Docker is available — continuing stack resume');
1402
+ }
746
1403
  // Reconcile orphan containers: stop/remove any omnitron.managed containers
747
1404
  // that don't belong to current registered projects/stacks (e.g. leftovers from
748
1405
  // a previous session or auto-restarted by Docker after a system reboot).
@@ -777,7 +1434,7 @@ export class OmnitronDaemon {
777
1434
  // Start each enabled stack
778
1435
  for (const stackName of enabledStacks) {
779
1436
  try {
780
- await projectService.startStack(project.name, stackName);
1437
+ await projectService.startStack(project.name, stackName, { source: 'boot' });
781
1438
  totalStacks++;
782
1439
  logger.info({ project: project.name, stack: stackName }, 'Stack started');
783
1440
  }
@@ -787,6 +1444,10 @@ export class OmnitronDaemon {
787
1444
  }
788
1445
  }
789
1446
  logger.info({ projects: allProjects.length, stacks: totalStacks }, 'All projects and stacks initialized');
1447
+ // Keep converging after boot: retry failed resumes (with backoff) and
1448
+ // re-start enabled stacks that later fall over. One-shot boot resume is
1449
+ // not enough — see the 2026-07-11 Docker-race incident.
1450
+ projectService.startEnabledStacksReconciler();
790
1451
  }
791
1452
  // ============================================================================
792
1453
  // Scheduled Tasks (Phase 2: titan-scheduler replaces bare setInterval)
@@ -803,7 +1464,8 @@ export class OmnitronDaemon {
803
1464
  const logManager = await container.resolveAsync(LOG_MANAGER_TOKEN);
804
1465
  // Master-only services (require PG)
805
1466
  const authService = !isSlave ? await container.resolveAsync(AUTH_SERVICE_TOKEN) : null;
806
- const alertService = !isSlave ? await container.resolveAsync(ALERT_SERVICE_TOKEN) : null;
1467
+ // Both roles: a node evaluates its own alarms on its SQLite (daemon.module.ts).
1468
+ const alertService = await container.resolveAsync(ALERT_SERVICE_TOKEN);
807
1469
  const fleetService = !isSlave ? await container.resolveAsync(FLEET_SERVICE_TOKEN) : null;
808
1470
  registerDaemonJobs(scheduler, {
809
1471
  logger,
@@ -814,6 +1476,10 @@ export class OmnitronDaemon {
814
1476
  fleetService,
815
1477
  logManager,
816
1478
  infraService: this.infraService,
1479
+ // Only a slave replicates. `bufferBatch` is itself a no-op for any
1480
+ // other role, but passing null keeps the job from registering at all
1481
+ // and makes the scheduler's own "skipped, because" line say so.
1482
+ syncService: isSlave ? this.syncService : null,
817
1483
  metricsInterval: config.monitoring.metrics.interval,
818
1484
  healthCheckInterval: config.monitoring.healthCheck.interval,
819
1485
  });
@@ -844,16 +1510,18 @@ export class OmnitronDaemon {
844
1510
  logger.warn({ error: err.message }, 'Telemetry relay failed to start');
845
1511
  }
846
1512
  }
847
- // Register self in fleet (master only — requires PG)
1513
+ // Register self in fleet (master only — requires PG). `registerSelf`, so
1514
+ // the service remembers which row is this daemon's: the fleet-heartbeat
1515
+ // job beats that row, and had only the literal 'self' before.
848
1516
  if (!isSlave) {
849
1517
  try {
850
1518
  const fleetService = await container.resolveAsync(FLEET_SERVICE_TOKEN);
851
- await fleetService.registerNode({
1519
+ await fleetService.registerSelf({
852
1520
  hostname: os.hostname(),
853
- address: this.dc.host === '0.0.0.0' ? '127.0.0.1' : this.dc.host,
1521
+ address: advertisedAddress(this.dc.host),
854
1522
  port: this.dc.port,
855
1523
  role: 'leader',
856
- metadata: { pid: process.pid, version: CLI_VERSION, httpPort: this.dc.httpPort },
1524
+ metadata: { pid: process.pid, version: CLI_VERSION, httpPort: this.dc.httpPort, rpcPort: this.rpcHttpPort },
857
1525
  });
858
1526
  }
859
1527
  catch {
@@ -898,31 +1566,50 @@ export class OmnitronDaemon {
898
1566
  // ============================================================================
899
1567
  // Health Monitor System Worker
900
1568
  // ============================================================================
1569
+ /**
1570
+ * Start the health-monitor system worker, and keep it started.
1571
+ *
1572
+ * Three things were wrong with the previous version, and they compounded
1573
+ * into one outage:
1574
+ *
1575
+ * - It returned SILENTLY when a precondition failed, so a daemon that
1576
+ * never checked a single node looked exactly like one that checked them
1577
+ * all. Each branch now says which precondition it was.
1578
+ * - Nothing watched the worker's process. When it died, the master kept
1579
+ * the proxy, and every fleet check — the console's Refresh, the Check
1580
+ * button, `omnitron node check` — answered
1581
+ * `TitanError: Service with id HealthMonitor@1.0.0 not found`. Observed
1582
+ * live: a fleet frozen at the worker's last report for two days, with
1583
+ * the console rendering it as current.
1584
+ * - The fallback timer existed only inside the catch for a FAILED SPAWN,
1585
+ * so a worker that started and later crashed had no fallback at all.
1586
+ *
1587
+ * The worker is now respawned with backoff, the proxy is dropped the moment
1588
+ * its process ends, and the daemon runs the checks itself in the meantime.
1589
+ */
901
1590
  async startHealthMonitorWorker(logger) {
902
- if (!this.app || !this.nodeManagerService || this.dc.role === 'slave')
1591
+ if (this.dc.role === 'slave')
1592
+ return; // By design: slaves do not manage a fleet.
1593
+ if (!this.app) {
1594
+ logger.warn({}, 'Health monitor not started — application is not available');
1595
+ return;
1596
+ }
1597
+ if (!this.nodeManagerService) {
1598
+ logger.warn({}, 'Health monitor not started — node manager was not constructed');
903
1599
  return;
1600
+ }
1601
+ const nodeManager = this.nodeManagerService;
904
1602
  try {
905
1603
  const titanPm = await import('@omnitron-dev/titan-pm');
906
1604
  const { SystemWorkerManager } = await import('../workers/system-worker-manager.js');
907
1605
  const { fileURLToPath } = await import('node:url');
908
1606
  const nodePath = await import('node:path');
1607
+ const { omnitronPgConnectionString } = await import('../database/connection.js');
909
1608
  // Resolve PM from DI
910
1609
  const pm = await this.app.container.resolveAsync(titanPm.PM_MANAGER_TOKEN);
911
1610
  const loggerModule = await this.app.container.resolveAsync(LOGGER_SERVICE_TOKEN);
912
- this.systemWorkerManager = new SystemWorkerManager(pm, loggerModule.logger.child({ component: 'system-workers' }));
913
- // Build health monitor config
914
- const hmDefaults = this.dc.healthMonitor ?? {};
915
- const healthConfig = {
916
- intervalMs: hmDefaults.intervalMs ?? 30_000,
917
- concurrency: hmDefaults.concurrency ?? 20,
918
- offlineTimeoutMs: hmDefaults.offlineTimeoutMs ?? 90_000,
919
- pingTimeout: hmDefaults.pingTimeout ?? 5_000,
920
- sshTimeout: hmDefaults.sshTimeout ?? 10_000,
921
- omnitronCheckTimeout: hmDefaults.omnitronCheckTimeout ?? 15_000,
922
- pingEnabled: hmDefaults.pingEnabled ?? true,
923
- retentionDays: hmDefaults.retentionDays ?? 7,
924
- dbUrl: 'postgresql://omnitron:omnitron@localhost:5480/omnitron',
925
- };
1611
+ const workerManager = new SystemWorkerManager(pm, loggerModule.logger.child({ component: 'system-workers' }));
1612
+ this.systemWorkerManager = workerManager;
926
1613
  // Get worker process path. The naive heuristic of "tsx in execArgv → .ts"
927
1614
  // is wrong: the daemon binary itself is loaded with `--import tsx/esm`
928
1615
  // (so transpilation works for any .ts in the tree), but the *daemon
@@ -932,52 +1619,114 @@ export class OmnitronDaemon {
932
1619
  const workersDir = nodePath.resolve(thisDir, '..', 'workers');
933
1620
  const tsPath = nodePath.join(workersDir, 'health-monitor-process.ts');
934
1621
  const jsPath = nodePath.join(workersDir, 'health-monitor-process.js');
935
- const workerPath = (await import('node:fs')).existsSync(jsPath)
1622
+ const fsMod = await import('node:fs');
1623
+ const workerPath = fsMod.existsSync(jsPath)
936
1624
  ? jsPath
937
- : (await import('node:fs')).existsSync(tsPath)
1625
+ : fsMod.existsSync(tsPath)
938
1626
  ? tsPath
939
1627
  : jsPath; // fall through to .js — error will be clear if missing
940
- // Convert nodes to check targets
941
- const nodesJson = JSON.stringify(await this.nodeManagerService.getNodeCheckTargets());
942
- const configJson = JSON.stringify(healthConfig);
943
1628
  const spawnOpts = {};
944
1629
  if (process.execArgv.some((a) => a.includes('tsx'))) {
945
1630
  spawnOpts.execArgv = ['--import', 'tsx/esm'];
946
1631
  }
947
- spawnOpts.startupTimeout = 30_000;
948
- const proxy = await this.systemWorkerManager.spawn('health-monitor', workerPath, { configJson, nodesJson }, spawnOpts);
949
- // Wire IPC: worker → master status cache updates via PM's public API
950
- const nodeManager = this.nodeManagerService;
951
- const eventBroadcaster = this.eventBroadcaster;
952
- this.systemWorkerManager.onMessage('health-monitor', (msg) => {
953
- if (msg?.type === 'health:status_batch' && Array.isArray(msg.summaries)) {
954
- nodeManager.updateStatusCacheFromWorker(msg.summaries);
955
- // Broadcast node status updates via WebSocket
956
- if (eventBroadcaster) {
957
- const online = msg.summaries.filter((s) => s.status === 'online').length;
958
- const degraded = msg.summaries.filter((s) => s.status === 'degraded').length;
959
- const offline = msg.summaries.filter((s) => s.status === 'offline').length;
960
- eventBroadcaster.broadcast(NODE_EVENTS.CHECK_COMPLETED, {
961
- nodeCount: msg.summaries.length,
962
- onlineCount: online,
963
- degradedCount: degraded,
964
- offlineCount: offline,
965
- });
1632
+ // What the worker has to do before it can report ready is create a
1633
+ // Titan application — a DI container, a module graph and a PG pool.
1634
+ // Managed applications get two to five minutes for that; this was
1635
+ // thirty seconds, and on a loaded host it expired on every attempt.
1636
+ // A deadline shorter than the work turns a healthy subsystem into one
1637
+ // that is started and killed for ever, which is what was observed:
1638
+ // fifty minutes of restart attempts, none of which could have finished.
1639
+ spawnOpts.startupTimeout =
1640
+ this.dc.healthMonitor?.startupTimeoutMs
1641
+ ?? DEFAULT_DAEMON_CONFIG.healthMonitor?.startupTimeoutMs
1642
+ ?? 120_000;
1643
+ this.healthWorkerStartupTimeout = spawnOpts.startupTimeout;
1644
+ /**
1645
+ * The worker's configuration.
1646
+ *
1647
+ * Read at every spawn, not once: the operator's check settings live in
1648
+ * the node manager and change at runtime. The fallbacks come from
1649
+ * `DEFAULT_DAEMON_CONFIG` rather than being written out again here —
1650
+ * the inline copies said 30 s and 7 days while the declared defaults
1651
+ * said 60 s and 90 days, so which numbers a deployment got depended on
1652
+ * whether its config file happened to mention `healthMonitor` at all.
1653
+ */
1654
+ const buildConfig = () => {
1655
+ const declared = DEFAULT_DAEMON_CONFIG.healthMonitor ?? {};
1656
+ const configured = this.dc.healthMonitor ?? {};
1657
+ const operator = nodeManager.getCheckConfig();
1658
+ return {
1659
+ intervalMs: configured.intervalMs ?? declared.intervalMs ?? 60_000,
1660
+ concurrency: operator.concurrency,
1661
+ offlineTimeoutMs: configured.offlineTimeoutMs ?? declared.offlineTimeoutMs ?? 90_000,
1662
+ pingTimeout: operator.pingTimeout,
1663
+ sshTimeout: operator.sshTimeout,
1664
+ omnitronCheckTimeout: operator.omnitronCheckTimeout,
1665
+ pingEnabled: operator.pingEnabled,
1666
+ retentionDays: configured.retentionDays ?? declared.retentionDays ?? 90,
1667
+ // Was a hard-coded DSN, password included, in this file — the sixth
1668
+ // copy of literals `database/connection.ts` exists to be the only
1669
+ // copy of. A daemon pointed elsewhere by OMNITRON_DATABASE_URL wrote
1670
+ // its check history to a database nothing else used.
1671
+ dbUrl: omnitronPgConnectionString(),
1672
+ };
1673
+ };
1674
+ this.healthWorkerSpawn = async () => {
1675
+ const healthConfig = buildConfig();
1676
+ const nodesJson = JSON.stringify(await nodeManager.getNodeCheckTargets());
1677
+ const proxy = await workerManager.spawn('health-monitor', workerPath, { configJson: JSON.stringify(healthConfig), nodesJson }, spawnOpts);
1678
+ // Wire IPC: worker → master status cache updates via PM's public API
1679
+ workerManager.onMessage('health-monitor', (msg) => {
1680
+ if (msg?.type === 'health:status_batch' && Array.isArray(msg.summaries)) {
1681
+ nodeManager.updateStatusCacheFromWorker(msg.summaries);
1682
+ this.broadcastNodeCheckCompleted(msg.summaries);
966
1683
  }
1684
+ });
1685
+ if (this.nodeManagerRpcService) {
1686
+ this.nodeManagerRpcService.setHealthWorkerProxy(proxy);
967
1687
  }
1688
+ this.healthWorkerRestartAttempts = 0;
1689
+ this.stopFallbackNodeChecks();
1690
+ logger.info({ intervalMs: healthConfig.intervalMs }, 'Health monitor system worker started');
1691
+ };
1692
+ // A worker that ends — crash or deliberate stop — must take its proxy
1693
+ // with it. `expected` is true only while the daemon is shutting down,
1694
+ // which is also when a respawn would be wrong.
1695
+ workerManager.onExit('health-monitor', (exit) => {
1696
+ this.nodeManagerRpcService?.setHealthWorkerProxy(null);
1697
+ if (exit.expected || this.isShuttingDown)
1698
+ return;
1699
+ this.startFallbackNodeChecks(logger);
1700
+ this.scheduleHealthMonitorRestart(logger);
1701
+ });
1702
+ // Operator changes to the check settings have to reach the process that
1703
+ // performs the checks. They reached a field on the node manager that
1704
+ // only the daemon's own fallback path read, so turning ping off in the
1705
+ // console turned nothing off.
1706
+ nodeManager.on('checkConfig:changed', () => {
1707
+ void callWorkerMethod(workerManager, 'updateConfig', JSON.stringify(buildConfig())).catch((err) => {
1708
+ logger.warn({ error: err.message }, 'Failed to push check config to health monitor');
1709
+ });
968
1710
  });
969
1711
  // Wire node CRUD events → sync to worker
970
1712
  const syncNodesToWorker = async () => {
1713
+ if (!workerManager.get('health-monitor'))
1714
+ return;
971
1715
  const targets = await nodeManager.getNodeCheckTargets();
972
- proxy.updateNodes(JSON.stringify(targets)).catch(() => { });
1716
+ await callWorkerMethod(workerManager, 'updateNodes', JSON.stringify(targets)).catch((err) => {
1717
+ logger.warn({ error: err.message }, 'Failed to sync node list to health monitor');
1718
+ });
973
1719
  };
974
1720
  nodeManager.on('node:added', syncNodesToWorker);
975
1721
  nodeManager.on('node:updated', syncNodesToWorker);
976
1722
  nodeManager.on('node:removed', syncNodesToWorker);
977
- // Wire RPC service to use worker proxy + PG repository for direct reads
1723
+ // A node changing state is the event the console subscribes to. Four of
1724
+ // the five NODE_EVENTS channels were declared and never emitted.
1725
+ nodeManager.on('node:health', (t) => {
1726
+ this.broadcastNodeTransition(t);
1727
+ });
1728
+ // Wire PG repository for direct history reads (bypasses worker)
978
1729
  if (this.nodeManagerRpcService) {
979
- this.nodeManagerRpcService.setHealthWorkerProxy(proxy);
980
- // Wire PG repository for direct history reads (bypasses worker)
981
1730
  try {
982
1731
  const db = await this.app.container.resolveAsync(OMNITRON_DB_TOKEN);
983
1732
  const { NodeHealthRepository } = await import('../services/node-health.repository.js');
@@ -987,20 +1736,102 @@ export class OmnitronDaemon {
987
1736
  logger.warn({}, 'Could not wire health repository — PG may not be ready');
988
1737
  }
989
1738
  }
990
- logger.info({ intervalMs: healthConfig.intervalMs }, 'Health monitor system worker started');
1739
+ // The console needs these to size its uptime bars against history that
1740
+ // actually exists.
1741
+ const bootConfig = buildConfig();
1742
+ nodeManager.setHistoryConfig({
1743
+ uptimeIntervalMs: this.dc.healthMonitor?.uptimeIntervalMs
1744
+ ?? DEFAULT_DAEMON_CONFIG.healthMonitor?.uptimeIntervalMs
1745
+ ?? 86_400_000,
1746
+ retentionDays: bootConfig.retentionDays,
1747
+ });
1748
+ await this.healthWorkerSpawn();
991
1749
  }
992
1750
  catch (err) {
993
1751
  logger.warn({ error: err.message }, 'Health monitor worker failed to start — falling back to direct checks');
994
- // Fallback: start legacy periodic checks if worker fails
995
- // This ensures node status still works even without the worker
996
- if (this.nodeManagerService) {
997
- const intervalMs = this.dc.healthMonitor?.intervalMs ?? 30_000;
998
- const timer = setInterval(() => void this.nodeManagerService?.checkAllNodes(), intervalMs);
999
- timer.unref();
1000
- void this.nodeManagerService.checkAllNodes();
1001
- }
1752
+ this.startFallbackNodeChecks(logger);
1753
+ if (this.healthWorkerSpawn)
1754
+ this.scheduleHealthMonitorRestart(logger);
1002
1755
  }
1003
1756
  }
1757
+ /**
1758
+ * Try the health-monitor worker again, backing off.
1759
+ *
1760
+ * Capped at a minute: the worker is cheap to start and the fleet view is
1761
+ * blind without it, but a worker that cannot start (a missing file, a bad
1762
+ * runtime) must not be respawned in a tight loop for the life of the daemon.
1763
+ */
1764
+ scheduleHealthMonitorRestart(logger) {
1765
+ if (this.healthWorkerRestartTimer || this.isShuttingDown || !this.healthWorkerSpawn)
1766
+ return;
1767
+ const attempt = ++this.healthWorkerRestartAttempts;
1768
+ const delay = Math.min(60_000, 2_000 * 2 ** Math.min(attempt - 1, 5));
1769
+ logger.warn({ attempt, delayMs: delay }, 'Health monitor worker is down — restarting');
1770
+ this.healthWorkerRestartTimer = setTimeout(() => {
1771
+ this.healthWorkerRestartTimer = null;
1772
+ if (this.isShuttingDown || !this.healthWorkerSpawn)
1773
+ return;
1774
+ this.healthWorkerSpawn().catch((err) => {
1775
+ logger.warn({ attempt, startupTimeoutMs: this.healthWorkerStartupTimeout, error: err.message }, 'Health monitor worker restart failed');
1776
+ this.scheduleHealthMonitorRestart(logger);
1777
+ });
1778
+ }, delay);
1779
+ this.healthWorkerRestartTimer.unref();
1780
+ }
1781
+ /**
1782
+ * Run node checks in the daemon while the worker is unavailable.
1783
+ *
1784
+ * Idempotent, and stopped as soon as the worker is back — two check loops
1785
+ * against the same fleet would double every remote host's load and write
1786
+ * two rows per interval.
1787
+ */
1788
+ startFallbackNodeChecks(logger) {
1789
+ if (this.fallbackNodeCheckTimer || !this.nodeManagerService)
1790
+ return;
1791
+ const declared = DEFAULT_DAEMON_CONFIG.healthMonitor ?? {};
1792
+ const intervalMs = this.dc.healthMonitor?.intervalMs ?? declared.intervalMs ?? 60_000;
1793
+ logger.warn({ intervalMs }, 'Checking nodes from the daemon until the health monitor returns');
1794
+ this.fallbackNodeCheckTimer = setInterval(() => {
1795
+ void this.nodeManagerService?.checkAllNodes();
1796
+ }, intervalMs);
1797
+ this.fallbackNodeCheckTimer.unref();
1798
+ void this.nodeManagerService.checkAllNodes();
1799
+ }
1800
+ stopFallbackNodeChecks() {
1801
+ if (!this.fallbackNodeCheckTimer)
1802
+ return;
1803
+ clearInterval(this.fallbackNodeCheckTimer);
1804
+ this.fallbackNodeCheckTimer = null;
1805
+ }
1806
+ /** Aggregate counts from a worker batch → one console event. */
1807
+ broadcastNodeCheckCompleted(summaries) {
1808
+ if (!this.eventBroadcaster)
1809
+ return;
1810
+ const count = (s) => summaries.filter((x) => x.status === s).length;
1811
+ this.eventBroadcaster.broadcast(NODE_EVENTS.CHECK_COMPLETED, {
1812
+ nodeCount: summaries.length,
1813
+ onlineCount: count('online'),
1814
+ degradedCount: count('degraded'),
1815
+ offlineCount: count('offline'),
1816
+ });
1817
+ }
1818
+ /** One node's health changing → the channel named after the new state. */
1819
+ broadcastNodeTransition(transition) {
1820
+ if (!this.eventBroadcaster)
1821
+ return;
1822
+ const payload = {
1823
+ nodeId: transition.nodeId,
1824
+ status: transition.status,
1825
+ ...(transition.previousStatus ? { previousStatus: transition.previousStatus } : {}),
1826
+ };
1827
+ this.eventBroadcaster.broadcast(NODE_EVENTS.STATUS_UPDATED, payload);
1828
+ const channel = transition.status === 'online' ? NODE_EVENTS.WENT_ONLINE
1829
+ : transition.status === 'offline' ? NODE_EVENTS.WENT_OFFLINE
1830
+ : transition.status === 'degraded' ? NODE_EVENTS.WENT_DEGRADED
1831
+ : null;
1832
+ if (channel)
1833
+ this.eventBroadcaster.broadcast(channel, payload);
1834
+ }
1004
1835
  // ============================================================================
1005
1836
  // Event Broadcasting (Phase 3: orchestrator events → WebSocket push)
1006
1837
  // ============================================================================
@@ -1096,16 +1927,53 @@ export class OmnitronDaemon {
1096
1927
  this.fileWatcher.start(apps);
1097
1928
  return this.fileWatcher.getWatchedApps();
1098
1929
  }
1930
+ /**
1931
+ * Point the file watcher at a reloaded config, if one is running.
1932
+ *
1933
+ * Public because `DaemonRpcService.reloadConfig` is the other half of the
1934
+ * same reload — the RPC and the SIGHUP handler must not diverge on what a
1935
+ * reload updates, which is exactly how the watcher came to be left out of
1936
+ * both.
1937
+ */
1938
+ applyWatchConfig(config) {
1939
+ this.fileWatcher?.applyConfig(config);
1940
+ }
1099
1941
  disableWatch() {
1100
1942
  if (this.fileWatcher) {
1101
1943
  this.fileWatcher.stop();
1102
1944
  this.fileWatcher = null;
1103
1945
  }
1104
1946
  }
1947
+ /**
1948
+ * Whether file watching is running, and over what.
1949
+ *
1950
+ * `enabled` said only that a `FileWatcher` OBJECT existed. Observed on this
1951
+ * host: `{ enabled: true, apps: [] }` — a watcher running over nothing,
1952
+ * reported to an operator as "watching". Everyone downstream read the
1953
+ * boolean and concluded their edits were being picked up.
1954
+ *
1955
+ * The watch set is built from `config.apps` — the daemon's OWN ecosystem
1956
+ * config. Apps that arrive through the project registry are not in it, so a
1957
+ * daemon serving projects watches none of them however the flag reads.
1958
+ * `watching` is the fact an operator actually wants; `enabled` keeps its
1959
+ * old meaning so existing callers do not silently change behaviour, and
1960
+ * `reason` says why the two can disagree.
1961
+ */
1105
1962
  getWatchStatus() {
1106
- if (!this.fileWatcher)
1107
- return { enabled: false, apps: [] };
1108
- return { enabled: true, apps: this.fileWatcher.getWatchedApps() };
1963
+ if (!this.fileWatcher) {
1964
+ return { enabled: false, watching: false, reason: 'the file watcher is not running', apps: [] };
1965
+ }
1966
+ const apps = this.fileWatcher.getWatchedApps();
1967
+ if (apps.length === 0) {
1968
+ return {
1969
+ enabled: true,
1970
+ watching: false,
1971
+ reason: 'the watcher is running but no app matched: the watch set comes from this daemon\'s own ' +
1972
+ 'ecosystem config, and apps registered through a project are not in it',
1973
+ apps,
1974
+ };
1975
+ }
1976
+ return { enabled: true, watching: true, apps };
1109
1977
  }
1110
1978
  async getConfig() {
1111
1979
  const { loadEcosystemConfig } = await import('../config/loader.js');
@@ -1116,7 +1984,7 @@ export class OmnitronDaemon {
1116
1984
  // ============================================================================
1117
1985
  /**
1118
1986
  * Trigger graceful shutdown via Application's lifecycle system.
1119
- * Called by DaemonRpcService.shutdown() for the `omnitron shutdown` CLI command.
1987
+ * Called by DaemonRpcService.shutdown() for the `omnitron down` CLI command.
1120
1988
  */
1121
1989
  async stop() {
1122
1990
  if (!this.app)
@@ -1125,6 +1993,67 @@ export class OmnitronDaemon {
1125
1993
  // Application doesn't process.exit() for Manual reason — do it explicitly
1126
1994
  process.exit(0);
1127
1995
  }
1996
+ /**
1997
+ * SIGHUP handler — reload the ecosystem config without taking the
1998
+ * daemon down. Follows the Unix convention (`man 7 signal`: "many
1999
+ * daemons interpret SIGHUP as a request to reread their configs").
2000
+ * Reloads via the same code path the `reloadConfig` RPC uses so
2001
+ * orchestrator + RPC service both pick up the new entries.
2002
+ */
2003
+ async reloadConfigOnHup() {
2004
+ if (!this.app)
2005
+ return;
2006
+ // Loading first, on its own: a malformed file must be rejected before
2007
+ // anything is touched, and that failure genuinely means "the daemon is as
2008
+ // it was".
2009
+ const { loadEcosystemConfig } = await import('../config/loader.js');
2010
+ const newConfig = await loadEcosystemConfig();
2011
+ // Applying is the other half, and it has no such guarantee — each
2012
+ // destination takes the config in turn, so a failure in the middle leaves
2013
+ // the daemon split. `applyReloadedConfig` names what applied and what did
2014
+ // not, because "reload failed" reads as "nothing changed" and that is the
2015
+ // wrong reading exactly when it matters.
2016
+ //
2017
+ // The handler used to end in `catch (err) { throw err; }`, which did
2018
+ // nothing whatever. Removing it is trivial; what it was standing in front
2019
+ // of is not.
2020
+ const { applyReloadedConfig } = await import('./config-reload.js');
2021
+ await applyReloadedConfig(newConfig, [
2022
+ {
2023
+ name: 'orchestrator',
2024
+ apply: async (cfg) => {
2025
+ const orchestrator = await this.app.container.resolveAsync(ORCHESTRATOR_TOKEN);
2026
+ orchestrator.setConfig(cfg);
2027
+ },
2028
+ },
2029
+ {
2030
+ name: 'file watcher',
2031
+ // The watcher holds its own copy of the app list. Left out, a reload
2032
+ // updated who gets restarted but not who is watched: an app added to
2033
+ // the config was never watched, and one removed from it kept its
2034
+ // watchers and went on triggering restarts.
2035
+ apply: (cfg) => {
2036
+ this.applyWatchConfig(cfg);
2037
+ },
2038
+ },
2039
+ {
2040
+ name: 'RPC snapshot',
2041
+ // Exposed under DAEMON_SERVICE_ID; the reloadConfig RPC path mutates
2042
+ // `this.config` directly, so we mirror the same write. Unresolvable is
2043
+ // not a failure here — the service may not be exposed yet.
2044
+ apply: async (cfg) => {
2045
+ const { DAEMON_SERVICE_ID } = await import('../config/defaults.js');
2046
+ const daemonRpc = await this.app.container
2047
+ .resolveAsync(DAEMON_SERVICE_ID)
2048
+ .catch(() => null);
2049
+ if (daemonRpc) {
2050
+ daemonRpc.config = cfg;
2051
+ }
2052
+ },
2053
+ },
2054
+ ]);
2055
+ console.info('[omnitron] SIGHUP — config reloaded');
2056
+ }
1128
2057
  /**
1129
2058
  * Register all daemon shutdown tasks with Application's lifecycle system.
1130
2059
  *
@@ -1199,6 +2128,20 @@ export class OmnitronDaemon {
1199
2128
  }
1200
2129
  catch { /* non-critical */ }
1201
2130
  }, ShutdownPriority.First);
2131
+ // A release build outlives its daemon otherwise.
2132
+ //
2133
+ // Every step runs in its own process group, detached, so `pnpm install`
2134
+ // and the gate suite carry on after the daemon that started them is
2135
+ // gone — holding ports, writing into a release directory nothing will
2136
+ // ever finish, and for a quarter of an hour. Stopped first, with the
2137
+ // file watcher, because ending them is instant and everything after
2138
+ // this is slower.
2139
+ app.registerShutdownTask('stop-release-builds', () => {
2140
+ // It says what it stopped through its own logger; a shutdown task that
2141
+ // resolved the logger module to say one line would be one more thing
2142
+ // that can fail while the daemon is going down.
2143
+ this.releaseService?.stopAll();
2144
+ }, ShutdownPriority.First);
1202
2145
  // --- Priority: VeryHigh (10) — Stop managed apps (fast — 5s per app max) ---
1203
2146
  app.registerShutdownTask('stop-managed-apps', async () => {
1204
2147
  const orchestrator = await container.resolveAsync(ORCHESTRATOR_TOKEN);
@@ -1216,11 +2159,6 @@ export class OmnitronDaemon {
1216
2159
  await mc.stop();
1217
2160
  }
1218
2161
  catch { /* non-critical */ }
1219
- try {
1220
- const alert = await container.resolveAsync(ALERT_SERVICE_TOKEN);
1221
- alert.stop();
1222
- }
1223
- catch { /* non-critical */ }
1224
2162
  try {
1225
2163
  const fleet = await container.resolveAsync(FLEET_SERVICE_TOKEN);
1226
2164
  fleet.stopHeartbeatSweep();
@@ -1234,6 +2172,22 @@ export class OmnitronDaemon {
1234
2172
  }, ShutdownPriority.High);
1235
2173
  // --- Priority: Normal (50) — System workers, node manager, sync, cluster ---
1236
2174
  app.registerShutdownTask('stop-system-services', async () => {
2175
+ // The mesh first: stop following the registry, then drop the
2176
+ // connections. Each one may hold an SSH tunnel, and a tunnel that
2177
+ // outlives its daemon holds a socket and a remote session open with
2178
+ // nothing left to notice.
2179
+ try {
2180
+ this.meshHandle?.stop();
2181
+ }
2182
+ catch { /* non-critical */ }
2183
+ this.meshHandle = null;
2184
+ if (this.slaveConnector) {
2185
+ try {
2186
+ await this.slaveConnector.dispose();
2187
+ }
2188
+ catch { /* non-critical */ }
2189
+ this.slaveConnector = null;
2190
+ }
1237
2191
  if (this.syncService) {
1238
2192
  try {
1239
2193
  await this.syncService.stop();
@@ -1251,6 +2205,15 @@ export class OmnitronDaemon {
1251
2205
  this.configSyncService.stopPeriodicSync();
1252
2206
  }
1253
2207
  catch { /* non-critical */ }
2208
+ // Before stopping the workers: a worker exiting on the way out must not
2209
+ // be read as a crash and rescheduled.
2210
+ this.isShuttingDown = true;
2211
+ this.healthWorkerSpawn = null;
2212
+ if (this.healthWorkerRestartTimer) {
2213
+ clearTimeout(this.healthWorkerRestartTimer);
2214
+ this.healthWorkerRestartTimer = null;
2215
+ }
2216
+ this.stopFallbackNodeChecks();
1254
2217
  if (this.systemWorkerManager) {
1255
2218
  try {
1256
2219
  await this.systemWorkerManager.stopAll();
@@ -1297,10 +2260,29 @@ export class OmnitronDaemon {
1297
2260
  }
1298
2261
  catch { /* non-critical */ }
1299
2262
  if (this.infraService) {
1300
- try {
1301
- await this.infraService.teardown();
2263
+ // A node's infrastructure outlives its daemon.
2264
+ //
2265
+ // `teardown()` is `docker rm`, not `docker stop`, and this task runs
2266
+ // on every shutdown — so on a node, restarting the daemon would
2267
+ // remove the stack's Postgres, Redis and MinIO. `omnitron fleet
2268
+ // upgrade` restarts every node's daemon, which would have made a
2269
+ // routine upgrade take the test environment down with it.
2270
+ //
2271
+ // The daemon is the supervisor, not the owner of the data plane's
2272
+ // lifetime. Locally it still tears down — `omnitron down` there means
2273
+ // "I am done for the day" — and on either, `omnitron infra down` is
2274
+ // the command that removes containers on purpose.
2275
+ if (this.dc.role === 'slave') {
2276
+ // Said on the way out, because the absence of a teardown is
2277
+ // otherwise indistinguishable from a teardown that failed.
2278
+ console.info('[omnitron] leaving this node\'s infrastructure running — the daemon does not own its lifetime');
2279
+ }
2280
+ else {
2281
+ try {
2282
+ await this.infraService.teardown();
2283
+ }
2284
+ catch { /* best-effort */ }
1302
2285
  }
1303
- catch { /* best-effort */ }
1304
2286
  this.infraService = null;
1305
2287
  }
1306
2288
  }, ShutdownPriority.VeryLow);
@@ -1310,4 +2292,22 @@ export class OmnitronDaemon {
1310
2292
  }, ShutdownPriority.Last);
1311
2293
  }
1312
2294
  }
2295
+ /**
2296
+ * Call a method on a system worker's proxy.
2297
+ *
2298
+ * `ServiceProxy` is typed with an index signature whose values may be an
2299
+ * async iterable as well as a function, so a direct `worker.updateConfig(…)`
2300
+ * does not type-check. Resolving through one place keeps the cast in one
2301
+ * place, and returns without throwing when the worker is not running — the
2302
+ * only correct answer for "tell the health monitor" when there is none.
2303
+ */
2304
+ async function callWorkerMethod(manager, method, ...args) {
2305
+ const worker = manager.get('health-monitor');
2306
+ if (!worker)
2307
+ return undefined;
2308
+ const fn = worker[method];
2309
+ if (typeof fn !== 'function')
2310
+ return undefined;
2311
+ return fn.apply(worker, args);
2312
+ }
1313
2313
  //# sourceMappingURL=daemon.js.map