@omnitron-dev/omnitron 0.2.0 → 0.3.1

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 (1299) hide show
  1. package/README.md +4 -0
  2. package/dist/BUILD.json +5 -0
  3. package/dist/cli/omnitron.js +375 -29
  4. package/dist/cli/omnitron.js.map +1 -1
  5. package/dist/cluster/cluster.rpc-service.d.ts +40 -0
  6. package/dist/cluster/cluster.rpc-service.d.ts.map +1 -1
  7. package/dist/cluster/cluster.rpc-service.js +49 -3
  8. package/dist/cluster/cluster.rpc-service.js.map +1 -1
  9. package/dist/cluster/config-sync.d.ts.map +1 -1
  10. package/dist/cluster/config-sync.js.map +1 -1
  11. package/dist/cluster/leader-election.d.ts +90 -2
  12. package/dist/cluster/leader-election.d.ts.map +1 -1
  13. package/dist/cluster/leader-election.js +176 -22
  14. package/dist/cluster/leader-election.js.map +1 -1
  15. package/dist/cluster/mesh-link.d.ts +136 -0
  16. package/dist/cluster/mesh-link.d.ts.map +1 -0
  17. package/dist/cluster/mesh-link.js +212 -0
  18. package/dist/cluster/mesh-link.js.map +1 -0
  19. package/dist/cluster/mesh.d.ts +59 -0
  20. package/dist/cluster/mesh.d.ts.map +1 -0
  21. package/dist/cluster/mesh.js +117 -0
  22. package/dist/cluster/mesh.js.map +1 -0
  23. package/dist/cluster/slave-connector.d.ts +193 -9
  24. package/dist/cluster/slave-connector.d.ts.map +1 -1
  25. package/dist/cluster/slave-connector.js +409 -37
  26. package/dist/cluster/slave-connector.js.map +1 -1
  27. package/dist/commands/audit.d.ts +65 -0
  28. package/dist/commands/audit.d.ts.map +1 -0
  29. package/dist/commands/audit.js +233 -0
  30. package/dist/commands/audit.js.map +1 -0
  31. package/dist/commands/backup.d.ts +4 -0
  32. package/dist/commands/backup.d.ts.map +1 -1
  33. package/dist/commands/backup.js +255 -33
  34. package/dist/commands/backup.js.map +1 -1
  35. package/dist/commands/cluster.d.ts.map +1 -1
  36. package/dist/commands/cluster.js +78 -47
  37. package/dist/commands/cluster.js.map +1 -1
  38. package/dist/commands/daemon-cmd.d.ts +7 -1
  39. package/dist/commands/daemon-cmd.d.ts.map +1 -1
  40. package/dist/commands/daemon-cmd.js +87 -21
  41. package/dist/commands/daemon-cmd.js.map +1 -1
  42. package/dist/commands/daemon-required.d.ts +34 -0
  43. package/dist/commands/daemon-required.d.ts.map +1 -0
  44. package/dist/commands/daemon-required.js +92 -0
  45. package/dist/commands/daemon-required.js.map +1 -0
  46. package/dist/commands/deploy.d.ts +70 -8
  47. package/dist/commands/deploy.d.ts.map +1 -1
  48. package/dist/commands/deploy.js +99 -64
  49. package/dist/commands/deploy.js.map +1 -1
  50. package/dist/commands/discover.d.ts +25 -4
  51. package/dist/commands/discover.d.ts.map +1 -1
  52. package/dist/commands/discover.js +155 -102
  53. package/dist/commands/discover.js.map +1 -1
  54. package/dist/commands/doctor.d.ts +293 -0
  55. package/dist/commands/doctor.d.ts.map +1 -0
  56. package/dist/commands/doctor.js +1706 -0
  57. package/dist/commands/doctor.js.map +1 -0
  58. package/dist/commands/env.d.ts +8 -1
  59. package/dist/commands/env.d.ts.map +1 -1
  60. package/dist/commands/env.js +13 -8
  61. package/dist/commands/env.js.map +1 -1
  62. package/dist/commands/exec.d.ts.map +1 -1
  63. package/dist/commands/exec.js +3 -2
  64. package/dist/commands/exec.js.map +1 -1
  65. package/dist/commands/fleet-asking.d.ts +87 -0
  66. package/dist/commands/fleet-asking.d.ts.map +1 -0
  67. package/dist/commands/fleet-asking.js +176 -0
  68. package/dist/commands/fleet-asking.js.map +1 -0
  69. package/dist/commands/fleet-recover.d.ts +2 -0
  70. package/dist/commands/fleet-recover.d.ts.map +1 -0
  71. package/dist/commands/fleet-recover.js +30 -0
  72. package/dist/commands/fleet-recover.js.map +1 -0
  73. package/dist/commands/fleet.d.ts +18 -0
  74. package/dist/commands/fleet.d.ts.map +1 -1
  75. package/dist/commands/fleet.js +314 -74
  76. package/dist/commands/fleet.js.map +1 -1
  77. package/dist/commands/health-check.d.ts +8 -6
  78. package/dist/commands/health-check.d.ts.map +1 -1
  79. package/dist/commands/health-check.js +26 -23
  80. package/dist/commands/health-check.js.map +1 -1
  81. package/dist/commands/health.d.ts.map +1 -1
  82. package/dist/commands/health.js +22 -8
  83. package/dist/commands/health.js.map +1 -1
  84. package/dist/commands/host-ingress.d.ts +3 -0
  85. package/dist/commands/host-ingress.d.ts.map +1 -0
  86. package/dist/commands/host-ingress.js +31 -0
  87. package/dist/commands/host-ingress.js.map +1 -0
  88. package/dist/commands/infra.d.ts +42 -1
  89. package/dist/commands/infra.d.ts.map +1 -1
  90. package/dist/commands/infra.js +418 -49
  91. package/dist/commands/infra.js.map +1 -1
  92. package/dist/commands/init.d.ts.map +1 -1
  93. package/dist/commands/init.js +21 -36
  94. package/dist/commands/init.js.map +1 -1
  95. package/dist/commands/inspect-graph.d.ts.map +1 -1
  96. package/dist/commands/inspect-graph.js +2 -6
  97. package/dist/commands/inspect-graph.js.map +1 -1
  98. package/dist/commands/inspect.d.ts.map +1 -1
  99. package/dist/commands/inspect.js +110 -14
  100. package/dist/commands/inspect.js.map +1 -1
  101. package/dist/commands/k8s.d.ts.map +1 -1
  102. package/dist/commands/k8s.js +23 -18
  103. package/dist/commands/k8s.js.map +1 -1
  104. package/dist/commands/kb.d.ts.map +1 -1
  105. package/dist/commands/kb.js +88 -29
  106. package/dist/commands/kb.js.map +1 -1
  107. package/dist/commands/list.d.ts.map +1 -1
  108. package/dist/commands/list.js +21 -9
  109. package/dist/commands/list.js.map +1 -1
  110. package/dist/commands/log-health.d.ts +120 -0
  111. package/dist/commands/log-health.d.ts.map +1 -0
  112. package/dist/commands/log-health.js +212 -0
  113. package/dist/commands/log-health.js.map +1 -0
  114. package/dist/commands/logs.d.ts +96 -2
  115. package/dist/commands/logs.d.ts.map +1 -1
  116. package/dist/commands/logs.js +522 -196
  117. package/dist/commands/logs.js.map +1 -1
  118. package/dist/commands/metrics.d.ts.map +1 -1
  119. package/dist/commands/metrics.js +35 -17
  120. package/dist/commands/metrics.js.map +1 -1
  121. package/dist/commands/monit.d.ts.map +1 -1
  122. package/dist/commands/monit.js +3 -2
  123. package/dist/commands/monit.js.map +1 -1
  124. package/dist/commands/node.d.ts +56 -0
  125. package/dist/commands/node.d.ts.map +1 -1
  126. package/dist/commands/node.js +297 -69
  127. package/dist/commands/node.js.map +1 -1
  128. package/dist/commands/output.d.ts +10 -0
  129. package/dist/commands/output.d.ts.map +1 -1
  130. package/dist/commands/output.js +41 -1
  131. package/dist/commands/output.js.map +1 -1
  132. package/dist/commands/pipeline.d.ts.map +1 -1
  133. package/dist/commands/pipeline.js +33 -19
  134. package/dist/commands/pipeline.js.map +1 -1
  135. package/dist/commands/project.d.ts +19 -0
  136. package/dist/commands/project.d.ts.map +1 -1
  137. package/dist/commands/project.js +125 -27
  138. package/dist/commands/project.js.map +1 -1
  139. package/dist/commands/release-store.d.ts +28 -0
  140. package/dist/commands/release-store.d.ts.map +1 -0
  141. package/dist/commands/release-store.js +158 -0
  142. package/dist/commands/release-store.js.map +1 -0
  143. package/dist/commands/release.d.ts +61 -0
  144. package/dist/commands/release.d.ts.map +1 -0
  145. package/dist/commands/release.js +555 -0
  146. package/dist/commands/release.js.map +1 -0
  147. package/dist/commands/reload.d.ts +16 -0
  148. package/dist/commands/reload.d.ts.map +1 -1
  149. package/dist/commands/reload.js +21 -5
  150. package/dist/commands/reload.js.map +1 -1
  151. package/dist/commands/remote.d.ts +40 -0
  152. package/dist/commands/remote.d.ts.map +1 -1
  153. package/dist/commands/remote.js +146 -35
  154. package/dist/commands/remote.js.map +1 -1
  155. package/dist/commands/restart.d.ts +16 -0
  156. package/dist/commands/restart.d.ts.map +1 -1
  157. package/dist/commands/restart.js +21 -5
  158. package/dist/commands/restart.js.map +1 -1
  159. package/dist/commands/scale.d.ts.map +1 -1
  160. package/dist/commands/scale.js +4 -3
  161. package/dist/commands/scale.js.map +1 -1
  162. package/dist/commands/secret.d.ts +54 -0
  163. package/dist/commands/secret.d.ts.map +1 -1
  164. package/dist/commands/secret.js +236 -34
  165. package/dist/commands/secret.js.map +1 -1
  166. package/dist/commands/service-units.d.ts +181 -0
  167. package/dist/commands/service-units.d.ts.map +1 -0
  168. package/dist/commands/service-units.js +291 -0
  169. package/dist/commands/service-units.js.map +1 -0
  170. package/dist/commands/service.d.ts +39 -0
  171. package/dist/commands/service.d.ts.map +1 -0
  172. package/dist/commands/service.js +370 -0
  173. package/dist/commands/service.js.map +1 -0
  174. package/dist/commands/spinner.d.ts +29 -0
  175. package/dist/commands/spinner.d.ts.map +1 -0
  176. package/dist/commands/spinner.js +44 -0
  177. package/dist/commands/spinner.js.map +1 -0
  178. package/dist/commands/stack.d.ts +49 -1
  179. package/dist/commands/stack.d.ts.map +1 -1
  180. package/dist/commands/stack.js +325 -12
  181. package/dist/commands/stack.js.map +1 -1
  182. package/dist/commands/start.d.ts +16 -0
  183. package/dist/commands/start.d.ts.map +1 -1
  184. package/dist/commands/start.js +20 -3
  185. package/dist/commands/start.js.map +1 -1
  186. package/dist/commands/status.d.ts +9 -0
  187. package/dist/commands/status.d.ts.map +1 -1
  188. package/dist/commands/status.js +45 -29
  189. package/dist/commands/status.js.map +1 -1
  190. package/dist/commands/stop.d.ts +16 -0
  191. package/dist/commands/stop.d.ts.map +1 -1
  192. package/dist/commands/stop.js +34 -6
  193. package/dist/commands/stop.js.map +1 -1
  194. package/dist/commands/tor.d.ts +14 -7
  195. package/dist/commands/tor.d.ts.map +1 -1
  196. package/dist/commands/tor.js +113 -33
  197. package/dist/commands/tor.js.map +1 -1
  198. package/dist/commands/up.d.ts +85 -1
  199. package/dist/commands/up.d.ts.map +1 -1
  200. package/dist/commands/up.js +180 -32
  201. package/dist/commands/up.js.map +1 -1
  202. package/dist/commands/webapp.d.ts.map +1 -1
  203. package/dist/commands/webapp.js +2 -1
  204. package/dist/commands/webapp.js.map +1 -1
  205. package/dist/config/defaults.d.ts +16 -0
  206. package/dist/config/defaults.d.ts.map +1 -1
  207. package/dist/config/defaults.js +26 -3
  208. package/dist/config/defaults.js.map +1 -1
  209. package/dist/config/define-ecosystem.d.ts.map +1 -1
  210. package/dist/config/define-ecosystem.js +2 -0
  211. package/dist/config/define-ecosystem.js.map +1 -1
  212. package/dist/config/index.d.ts +1 -1
  213. package/dist/config/index.d.ts.map +1 -1
  214. package/dist/config/index.js +1 -1
  215. package/dist/config/index.js.map +1 -1
  216. package/dist/config/loader.d.ts +45 -1
  217. package/dist/config/loader.d.ts.map +1 -1
  218. package/dist/config/loader.js +64 -6
  219. package/dist/config/loader.js.map +1 -1
  220. package/dist/config/omnitron-config.schema.d.ts.map +1 -1
  221. package/dist/config/omnitron-config.schema.js +16 -0
  222. package/dist/config/omnitron-config.schema.js.map +1 -1
  223. package/dist/config/types.d.ts +416 -44
  224. package/dist/config/types.d.ts.map +1 -1
  225. package/dist/daemon/config-reload.d.ts +27 -0
  226. package/dist/daemon/config-reload.d.ts.map +1 -0
  227. package/dist/daemon/config-reload.js +42 -0
  228. package/dist/daemon/config-reload.js.map +1 -0
  229. package/dist/daemon/daemon-client.d.ts +136 -2
  230. package/dist/daemon/daemon-client.d.ts.map +1 -1
  231. package/dist/daemon/daemon-client.js +307 -21
  232. package/dist/daemon/daemon-client.js.map +1 -1
  233. package/dist/daemon/daemon-entry.js +90 -30
  234. package/dist/daemon/daemon-entry.js.map +1 -1
  235. package/dist/daemon/daemon-scheduler.d.ts +4 -0
  236. package/dist/daemon/daemon-scheduler.d.ts.map +1 -1
  237. package/dist/daemon/daemon-scheduler.js +137 -7
  238. package/dist/daemon/daemon-scheduler.js.map +1 -1
  239. package/dist/daemon/daemon-state-store.service.d.ts +263 -0
  240. package/dist/daemon/daemon-state-store.service.d.ts.map +1 -0
  241. package/dist/daemon/daemon-state-store.service.js +446 -0
  242. package/dist/daemon/daemon-state-store.service.js.map +1 -0
  243. package/dist/daemon/daemon.d.ts +194 -4
  244. package/dist/daemon/daemon.d.ts.map +1 -1
  245. package/dist/daemon/daemon.js +1160 -157
  246. package/dist/daemon/daemon.js.map +1 -1
  247. package/dist/daemon/daemon.module.d.ts.map +1 -1
  248. package/dist/daemon/daemon.module.js +163 -85
  249. package/dist/daemon/daemon.module.js.map +1 -1
  250. package/dist/daemon/daemon.rpc-service.d.ts +90 -1
  251. package/dist/daemon/daemon.rpc-service.d.ts.map +1 -1
  252. package/dist/daemon/daemon.rpc-service.js +439 -97
  253. package/dist/daemon/daemon.rpc-service.js.map +1 -1
  254. package/dist/daemon/pid-manager.d.ts +16 -3
  255. package/dist/daemon/pid-manager.d.ts.map +1 -1
  256. package/dist/daemon/pid-manager.js +31 -4
  257. package/dist/daemon/pid-manager.js.map +1 -1
  258. package/dist/daemon/spawn-daemon.d.ts +73 -0
  259. package/dist/daemon/spawn-daemon.d.ts.map +1 -0
  260. package/dist/daemon/spawn-daemon.js +101 -0
  261. package/dist/daemon/spawn-daemon.js.map +1 -0
  262. package/dist/daemon/state-store.d.ts +65 -3
  263. package/dist/daemon/state-store.d.ts.map +1 -1
  264. package/dist/daemon/state-store.js +100 -58
  265. package/dist/daemon/state-store.js.map +1 -1
  266. package/dist/database/connection.d.ts +71 -0
  267. package/dist/database/connection.d.ts.map +1 -0
  268. package/dist/database/connection.js +129 -0
  269. package/dist/database/connection.js.map +1 -0
  270. package/dist/database/dialect.d.ts +60 -0
  271. package/dist/database/dialect.d.ts.map +1 -0
  272. package/dist/database/dialect.js +80 -0
  273. package/dist/database/dialect.js.map +1 -0
  274. package/dist/database/migrate.d.ts +5 -2
  275. package/dist/database/migrate.d.ts.map +1 -1
  276. package/dist/database/migrate.js +23 -33
  277. package/dist/database/migrate.js.map +1 -1
  278. package/dist/database/migration-runner.d.ts +50 -0
  279. package/dist/database/migration-runner.d.ts.map +1 -0
  280. package/dist/database/migration-runner.js +143 -0
  281. package/dist/database/migration-runner.js.map +1 -0
  282. package/dist/database/migrations/006_login_throttle.d.ts +17 -0
  283. package/dist/database/migrations/006_login_throttle.d.ts.map +1 -0
  284. package/dist/database/migrations/006_login_throttle.js +33 -0
  285. package/dist/database/migrations/006_login_throttle.js.map +1 -0
  286. package/dist/database/migrations/007_sync_ingested.d.ts +21 -0
  287. package/dist/database/migrations/007_sync_ingested.d.ts.map +1 -0
  288. package/dist/database/migrations/007_sync_ingested.js +39 -0
  289. package/dist/database/migrations/007_sync_ingested.js.map +1 -0
  290. package/dist/database/migrations/008_node_health_version_text.d.ts +24 -0
  291. package/dist/database/migrations/008_node_health_version_text.d.ts.map +1 -0
  292. package/dist/database/migrations/008_node_health_version_text.js +35 -0
  293. package/dist/database/migrations/008_node_health_version_text.js.map +1 -0
  294. package/dist/database/migrations/009_node_health_unmeasured.d.ts +40 -0
  295. package/dist/database/migrations/009_node_health_unmeasured.d.ts.map +1 -0
  296. package/dist/database/migrations/009_node_health_unmeasured.js +63 -0
  297. package/dist/database/migrations/009_node_health_unmeasured.js.map +1 -0
  298. package/dist/database/migrations/010_metrics_raw_had_no_reader.d.ts +22 -0
  299. package/dist/database/migrations/010_metrics_raw_had_no_reader.d.ts.map +1 -0
  300. package/dist/database/migrations/010_metrics_raw_had_no_reader.js +26 -0
  301. package/dist/database/migrations/010_metrics_raw_had_no_reader.js.map +1 -0
  302. package/dist/database/migrations/011_alerts_nobody_had_to_write.d.ts +56 -0
  303. package/dist/database/migrations/011_alerts_nobody_had_to_write.d.ts.map +1 -0
  304. package/dist/database/migrations/011_alerts_nobody_had_to_write.js +79 -0
  305. package/dist/database/migrations/011_alerts_nobody_had_to_write.js.map +1 -0
  306. package/dist/database/migrations/012_a_disk_nobody_watched.d.ts +26 -0
  307. package/dist/database/migrations/012_a_disk_nobody_watched.d.ts.map +1 -0
  308. package/dist/database/migrations/012_a_disk_nobody_watched.js +40 -0
  309. package/dist/database/migrations/012_a_disk_nobody_watched.js.map +1 -0
  310. package/dist/database/migrations/013_an_alarm_delivered.d.ts +14 -0
  311. package/dist/database/migrations/013_an_alarm_delivered.d.ts.map +1 -0
  312. package/dist/database/migrations/013_an_alarm_delivered.js +28 -0
  313. package/dist/database/migrations/013_an_alarm_delivered.js.map +1 -0
  314. package/dist/database/migrations/index.d.ts +21 -0
  315. package/dist/database/migrations/index.d.ts.map +1 -0
  316. package/dist/database/migrations/index.js +47 -0
  317. package/dist/database/migrations/index.js.map +1 -0
  318. package/dist/database/schema.d.ts +27 -11
  319. package/dist/database/schema.d.ts.map +1 -1
  320. package/dist/database/sqlite-date-binding.d.ts +71 -0
  321. package/dist/database/sqlite-date-binding.d.ts.map +1 -0
  322. package/dist/database/sqlite-date-binding.js +174 -0
  323. package/dist/database/sqlite-date-binding.js.map +1 -0
  324. package/dist/execution/data-channel.d.ts +48 -0
  325. package/dist/execution/data-channel.d.ts.map +1 -0
  326. package/dist/execution/data-channel.js +55 -0
  327. package/dist/execution/data-channel.js.map +1 -0
  328. package/dist/execution/execution.service.d.ts +146 -0
  329. package/dist/execution/execution.service.d.ts.map +1 -1
  330. package/dist/execution/execution.service.js +330 -10
  331. package/dist/execution/execution.service.js.map +1 -1
  332. package/dist/index.d.ts +1 -1
  333. package/dist/index.d.ts.map +1 -1
  334. package/dist/infrastructure/bare-metal-plan.d.ts +208 -0
  335. package/dist/infrastructure/bare-metal-plan.d.ts.map +1 -0
  336. package/dist/infrastructure/bare-metal-plan.js +363 -0
  337. package/dist/infrastructure/bare-metal-plan.js.map +1 -0
  338. package/dist/infrastructure/bare-metal-runner.d.ts +65 -0
  339. package/dist/infrastructure/bare-metal-runner.d.ts.map +1 -0
  340. package/dist/infrastructure/bare-metal-runner.js +354 -0
  341. package/dist/infrastructure/bare-metal-runner.js.map +1 -0
  342. package/dist/infrastructure/config-payload.d.ts +149 -0
  343. package/dist/infrastructure/config-payload.d.ts.map +1 -0
  344. package/dist/infrastructure/config-payload.js +235 -0
  345. package/dist/infrastructure/config-payload.js.map +1 -0
  346. package/dist/infrastructure/container-runtime.d.ts +170 -10
  347. package/dist/infrastructure/container-runtime.d.ts.map +1 -1
  348. package/dist/infrastructure/container-runtime.js +565 -52
  349. package/dist/infrastructure/container-runtime.js.map +1 -1
  350. package/dist/infrastructure/gateway-reachability.d.ts +85 -0
  351. package/dist/infrastructure/gateway-reachability.d.ts.map +1 -0
  352. package/dist/infrastructure/gateway-reachability.js +116 -0
  353. package/dist/infrastructure/gateway-reachability.js.map +1 -0
  354. package/dist/infrastructure/gateway-redis-db.d.ts +29 -0
  355. package/dist/infrastructure/gateway-redis-db.d.ts.map +1 -0
  356. package/dist/infrastructure/gateway-redis-db.js +32 -0
  357. package/dist/infrastructure/gateway-redis-db.js.map +1 -0
  358. package/dist/infrastructure/host-ingress-config.d.ts +12 -0
  359. package/dist/infrastructure/host-ingress-config.d.ts.map +1 -0
  360. package/dist/infrastructure/host-ingress-config.js +197 -0
  361. package/dist/infrastructure/host-ingress-config.js.map +1 -0
  362. package/dist/infrastructure/host-ingress-lock.d.ts +4 -0
  363. package/dist/infrastructure/host-ingress-lock.d.ts.map +1 -0
  364. package/dist/infrastructure/host-ingress-lock.js +36 -0
  365. package/dist/infrastructure/host-ingress-lock.js.map +1 -0
  366. package/dist/infrastructure/host-ingress-runtime.d.ts +25 -0
  367. package/dist/infrastructure/host-ingress-runtime.d.ts.map +1 -0
  368. package/dist/infrastructure/host-ingress-runtime.js +114 -0
  369. package/dist/infrastructure/host-ingress-runtime.js.map +1 -0
  370. package/dist/infrastructure/host-ingress-takeover.d.ts +18 -0
  371. package/dist/infrastructure/host-ingress-takeover.d.ts.map +1 -0
  372. package/dist/infrastructure/host-ingress-takeover.js +142 -0
  373. package/dist/infrastructure/host-ingress-takeover.js.map +1 -0
  374. package/dist/infrastructure/host-ingress-types.d.ts +173 -0
  375. package/dist/infrastructure/host-ingress-types.d.ts.map +1 -0
  376. package/dist/infrastructure/host-ingress-types.js +2 -0
  377. package/dist/infrastructure/host-ingress-types.js.map +1 -0
  378. package/dist/infrastructure/host-ingress.service.d.ts +47 -0
  379. package/dist/infrastructure/host-ingress.service.d.ts.map +1 -0
  380. package/dist/infrastructure/host-ingress.service.js +514 -0
  381. package/dist/infrastructure/host-ingress.service.js.map +1 -0
  382. package/dist/infrastructure/host-inspection.d.ts +223 -0
  383. package/dist/infrastructure/host-inspection.d.ts.map +1 -0
  384. package/dist/infrastructure/host-inspection.js +501 -0
  385. package/dist/infrastructure/host-inspection.js.map +1 -0
  386. package/dist/infrastructure/infrastructure-gate.d.ts.map +1 -1
  387. package/dist/infrastructure/infrastructure.service.d.ts +223 -7
  388. package/dist/infrastructure/infrastructure.service.d.ts.map +1 -1
  389. package/dist/infrastructure/infrastructure.service.js +500 -93
  390. package/dist/infrastructure/infrastructure.service.js.map +1 -1
  391. package/dist/infrastructure/known-machines.d.ts +76 -0
  392. package/dist/infrastructure/known-machines.d.ts.map +1 -0
  393. package/dist/infrastructure/known-machines.js +94 -0
  394. package/dist/infrastructure/known-machines.js.map +1 -0
  395. package/dist/infrastructure/node-credentials.d.ts +45 -0
  396. package/dist/infrastructure/node-credentials.d.ts.map +1 -0
  397. package/dist/infrastructure/node-credentials.js +80 -0
  398. package/dist/infrastructure/node-credentials.js.map +1 -0
  399. package/dist/infrastructure/orphan-containers.d.ts +99 -0
  400. package/dist/infrastructure/orphan-containers.d.ts.map +1 -0
  401. package/dist/infrastructure/orphan-containers.js +106 -0
  402. package/dist/infrastructure/orphan-containers.js.map +1 -0
  403. package/dist/infrastructure/phantom-endpoint-janitor.d.ts +20 -0
  404. package/dist/infrastructure/phantom-endpoint-janitor.d.ts.map +1 -1
  405. package/dist/infrastructure/phantom-endpoint-janitor.js +56 -6
  406. package/dist/infrastructure/phantom-endpoint-janitor.js.map +1 -1
  407. package/dist/infrastructure/presets/gateway-openresty.d.ts.map +1 -1
  408. package/dist/infrastructure/presets/gateway-openresty.js +29 -3
  409. package/dist/infrastructure/presets/gateway-openresty.js.map +1 -1
  410. package/dist/infrastructure/presets/minio.d.ts.map +1 -1
  411. package/dist/infrastructure/presets/minio.js +36 -3
  412. package/dist/infrastructure/presets/minio.js.map +1 -1
  413. package/dist/infrastructure/presets/postgres.d.ts.map +1 -1
  414. package/dist/infrastructure/presets/postgres.js +69 -2
  415. package/dist/infrastructure/presets/postgres.js.map +1 -1
  416. package/dist/infrastructure/presets/redis.d.ts.map +1 -1
  417. package/dist/infrastructure/presets/redis.js +13 -0
  418. package/dist/infrastructure/presets/redis.js.map +1 -1
  419. package/dist/infrastructure/presets/registry.d.ts.map +1 -1
  420. package/dist/infrastructure/presets/registry.js +81 -12
  421. package/dist/infrastructure/presets/registry.js.map +1 -1
  422. package/dist/infrastructure/presets/tor.d.ts +38 -0
  423. package/dist/infrastructure/presets/tor.d.ts.map +1 -1
  424. package/dist/infrastructure/presets/tor.js +78 -69
  425. package/dist/infrastructure/presets/tor.js.map +1 -1
  426. package/dist/infrastructure/presets/torrc.d.ts +54 -0
  427. package/dist/infrastructure/presets/torrc.d.ts.map +1 -0
  428. package/dist/infrastructure/presets/torrc.js +153 -0
  429. package/dist/infrastructure/presets/torrc.js.map +1 -0
  430. package/dist/infrastructure/presets/types.d.ts +17 -0
  431. package/dist/infrastructure/presets/types.d.ts.map +1 -1
  432. package/dist/infrastructure/provisioning-outcome.d.ts +85 -0
  433. package/dist/infrastructure/provisioning-outcome.d.ts.map +1 -0
  434. package/dist/infrastructure/provisioning-outcome.js +122 -0
  435. package/dist/infrastructure/provisioning-outcome.js.map +1 -0
  436. package/dist/infrastructure/server-registry.d.ts +27 -0
  437. package/dist/infrastructure/server-registry.d.ts.map +1 -1
  438. package/dist/infrastructure/server-registry.js +60 -5
  439. package/dist/infrastructure/server-registry.js.map +1 -1
  440. package/dist/infrastructure/service-binding.d.ts +60 -0
  441. package/dist/infrastructure/service-binding.d.ts.map +1 -0
  442. package/dist/infrastructure/service-binding.js +90 -0
  443. package/dist/infrastructure/service-binding.js.map +1 -0
  444. package/dist/infrastructure/service-credentials.d.ts +105 -0
  445. package/dist/infrastructure/service-credentials.d.ts.map +1 -0
  446. package/dist/infrastructure/service-credentials.js +141 -0
  447. package/dist/infrastructure/service-credentials.js.map +1 -0
  448. package/dist/infrastructure/service-resolver.d.ts +166 -12
  449. package/dist/infrastructure/service-resolver.d.ts.map +1 -1
  450. package/dist/infrastructure/service-resolver.js +535 -82
  451. package/dist/infrastructure/service-resolver.js.map +1 -1
  452. package/dist/infrastructure/shipped-config.d.ts +43 -0
  453. package/dist/infrastructure/shipped-config.d.ts.map +1 -0
  454. package/dist/infrastructure/shipped-config.js +159 -0
  455. package/dist/infrastructure/shipped-config.js.map +1 -0
  456. package/dist/infrastructure/stack-infra-manager.d.ts +16 -1
  457. package/dist/infrastructure/stack-infra-manager.d.ts.map +1 -1
  458. package/dist/infrastructure/stack-infra-manager.js +81 -53
  459. package/dist/infrastructure/stack-infra-manager.js.map +1 -1
  460. package/dist/infrastructure/types.d.ts +263 -6
  461. package/dist/infrastructure/types.d.ts.map +1 -1
  462. package/dist/mcp/mcp-bridge.d.ts.map +1 -1
  463. package/dist/mcp/mcp-bridge.js.map +1 -1
  464. package/dist/mcp/tool-groups/apps.tools.d.ts +2 -1
  465. package/dist/mcp/tool-groups/apps.tools.d.ts.map +1 -1
  466. package/dist/mcp/tool-groups/apps.tools.js +2 -2
  467. package/dist/mcp/tool-groups/apps.tools.js.map +1 -1
  468. package/dist/mcp/tool-groups/infra.tools.d.ts +16 -2
  469. package/dist/mcp/tool-groups/infra.tools.d.ts.map +1 -1
  470. package/dist/mcp/tool-groups/infra.tools.js +51 -41
  471. package/dist/mcp/tool-groups/infra.tools.js.map +1 -1
  472. package/dist/mcp/tool-groups/kb.tools.d.ts.map +1 -1
  473. package/dist/mcp/tool-groups/kb.tools.js +4 -6
  474. package/dist/mcp/tool-groups/kb.tools.js.map +1 -1
  475. package/dist/mcp/tool-groups/management.tools.d.ts +20 -3
  476. package/dist/mcp/tool-groups/management.tools.d.ts.map +1 -1
  477. package/dist/mcp/tool-groups/management.tools.js +158 -125
  478. package/dist/mcp/tool-groups/management.tools.js.map +1 -1
  479. package/dist/mcp/tool-groups/monitoring.tools.d.ts +10 -1
  480. package/dist/mcp/tool-groups/monitoring.tools.d.ts.map +1 -1
  481. package/dist/mcp/tool-groups/monitoring.tools.js +65 -18
  482. package/dist/mcp/tool-groups/monitoring.tools.js.map +1 -1
  483. package/dist/mcp/unavailable-tools.d.ts +35 -0
  484. package/dist/mcp/unavailable-tools.d.ts.map +1 -0
  485. package/dist/mcp/unavailable-tools.js +59 -0
  486. package/dist/mcp/unavailable-tools.js.map +1 -0
  487. package/dist/monitoring/app-health.indicator.d.ts.map +1 -1
  488. package/dist/monitoring/app-health.indicator.js +12 -0
  489. package/dist/monitoring/app-health.indicator.js.map +1 -1
  490. package/dist/monitoring/desktop-notifier.d.ts +26 -0
  491. package/dist/monitoring/desktop-notifier.d.ts.map +1 -0
  492. package/dist/monitoring/desktop-notifier.js +49 -0
  493. package/dist/monitoring/desktop-notifier.js.map +1 -0
  494. package/dist/monitoring/docker-health.indicator.d.ts +17 -1
  495. package/dist/monitoring/docker-health.indicator.d.ts.map +1 -1
  496. package/dist/monitoring/docker-health.indicator.js +38 -8
  497. package/dist/monitoring/docker-health.indicator.js.map +1 -1
  498. package/dist/monitoring/event-loop-stall.indicator.d.ts +26 -0
  499. package/dist/monitoring/event-loop-stall.indicator.d.ts.map +1 -0
  500. package/dist/monitoring/event-loop-stall.indicator.js +47 -0
  501. package/dist/monitoring/event-loop-stall.indicator.js.map +1 -0
  502. package/dist/monitoring/event-loop-watch.d.ts +124 -0
  503. package/dist/monitoring/event-loop-watch.d.ts.map +1 -0
  504. package/dist/monitoring/event-loop-watch.js +179 -0
  505. package/dist/monitoring/event-loop-watch.js.map +1 -0
  506. package/dist/monitoring/host-disk.d.ts +6 -0
  507. package/dist/monitoring/host-disk.d.ts.map +1 -0
  508. package/dist/monitoring/host-disk.js +26 -0
  509. package/dist/monitoring/host-disk.js.map +1 -0
  510. package/dist/monitoring/log-line.d.ts +188 -0
  511. package/dist/monitoring/log-line.d.ts.map +1 -0
  512. package/dist/monitoring/log-line.js +458 -0
  513. package/dist/monitoring/log-line.js.map +1 -0
  514. package/dist/monitoring/log-manager.d.ts +89 -8
  515. package/dist/monitoring/log-manager.d.ts.map +1 -1
  516. package/dist/monitoring/log-manager.js +246 -77
  517. package/dist/monitoring/log-manager.js.map +1 -1
  518. package/dist/observability/metrics-bridge.d.ts.map +1 -1
  519. package/dist/observability/metrics-server.d.ts.map +1 -1
  520. package/dist/operations.d.ts +16 -0
  521. package/dist/operations.d.ts.map +1 -0
  522. package/dist/operations.js +13 -0
  523. package/dist/operations.js.map +1 -0
  524. package/dist/orchestrator/app-handle.d.ts +157 -2
  525. package/dist/orchestrator/app-handle.d.ts.map +1 -1
  526. package/dist/orchestrator/app-handle.js +165 -3
  527. package/dist/orchestrator/app-handle.js.map +1 -1
  528. package/dist/orchestrator/app-health.d.ts +33 -0
  529. package/dist/orchestrator/app-health.d.ts.map +1 -0
  530. package/dist/orchestrator/app-health.js +74 -0
  531. package/dist/orchestrator/app-health.js.map +1 -0
  532. package/dist/orchestrator/app-traffic.d.ts +14 -0
  533. package/dist/orchestrator/app-traffic.d.ts.map +1 -0
  534. package/dist/orchestrator/app-traffic.js +33 -0
  535. package/dist/orchestrator/app-traffic.js.map +1 -0
  536. package/dist/orchestrator/bootstrap-loader-child.d.ts +19 -0
  537. package/dist/orchestrator/bootstrap-loader-child.d.ts.map +1 -0
  538. package/dist/orchestrator/bootstrap-loader-child.js +43 -0
  539. package/dist/orchestrator/bootstrap-loader-child.js.map +1 -0
  540. package/dist/orchestrator/bootstrap-loader.d.ts +72 -0
  541. package/dist/orchestrator/bootstrap-loader.d.ts.map +1 -1
  542. package/dist/orchestrator/bootstrap-loader.js +350 -5
  543. package/dist/orchestrator/bootstrap-loader.js.map +1 -1
  544. package/dist/orchestrator/bootstrap-process.d.ts +99 -4
  545. package/dist/orchestrator/bootstrap-process.d.ts.map +1 -1
  546. package/dist/orchestrator/bootstrap-process.js +332 -20
  547. package/dist/orchestrator/bootstrap-process.js.map +1 -1
  548. package/dist/orchestrator/build-service.d.ts +6 -12
  549. package/dist/orchestrator/build-service.d.ts.map +1 -1
  550. package/dist/orchestrator/build-service.js +120 -83
  551. package/dist/orchestrator/build-service.js.map +1 -1
  552. package/dist/orchestrator/call-exposed-service.d.ts +7 -0
  553. package/dist/orchestrator/call-exposed-service.d.ts.map +1 -0
  554. package/dist/orchestrator/call-exposed-service.js +50 -0
  555. package/dist/orchestrator/call-exposed-service.js.map +1 -0
  556. package/dist/orchestrator/classic-launcher.d.ts +1 -0
  557. package/dist/orchestrator/classic-launcher.d.ts.map +1 -1
  558. package/dist/orchestrator/classic-launcher.js +40 -4
  559. package/dist/orchestrator/classic-launcher.js.map +1 -1
  560. package/dist/orchestrator/file-watcher.d.ts +79 -1
  561. package/dist/orchestrator/file-watcher.d.ts.map +1 -1
  562. package/dist/orchestrator/file-watcher.js +193 -7
  563. package/dist/orchestrator/file-watcher.js.map +1 -1
  564. package/dist/orchestrator/http-routes.d.ts +4 -0
  565. package/dist/orchestrator/http-routes.d.ts.map +1 -0
  566. package/dist/orchestrator/http-routes.js +19 -0
  567. package/dist/orchestrator/http-routes.js.map +1 -0
  568. package/dist/orchestrator/module-worker-process.d.ts.map +1 -1
  569. package/dist/orchestrator/module-worker-process.js +10 -0
  570. package/dist/orchestrator/module-worker-process.js.map +1 -1
  571. package/dist/orchestrator/orchestrator.service.d.ts +385 -11
  572. package/dist/orchestrator/orchestrator.service.d.ts.map +1 -1
  573. package/dist/orchestrator/orchestrator.service.js +1738 -364
  574. package/dist/orchestrator/orchestrator.service.js.map +1 -1
  575. package/dist/orchestrator/owned-pids.d.ts +83 -0
  576. package/dist/orchestrator/owned-pids.d.ts.map +1 -0
  577. package/dist/orchestrator/owned-pids.js +93 -0
  578. package/dist/orchestrator/owned-pids.js.map +1 -0
  579. package/dist/orchestrator/port-conflict.d.ts +102 -0
  580. package/dist/orchestrator/port-conflict.d.ts.map +1 -0
  581. package/dist/orchestrator/port-conflict.js +131 -0
  582. package/dist/orchestrator/port-conflict.js.map +1 -0
  583. package/dist/orchestrator/process-janitor.d.ts +78 -4
  584. package/dist/orchestrator/process-janitor.d.ts.map +1 -1
  585. package/dist/orchestrator/process-janitor.js +246 -30
  586. package/dist/orchestrator/process-janitor.js.map +1 -1
  587. package/dist/orchestrator/ps-metrics.d.ts +37 -0
  588. package/dist/orchestrator/ps-metrics.d.ts.map +1 -0
  589. package/dist/orchestrator/ps-metrics.js +60 -0
  590. package/dist/orchestrator/ps-metrics.js.map +1 -0
  591. package/dist/orchestrator/service-router.d.ts +58 -2
  592. package/dist/orchestrator/service-router.d.ts.map +1 -1
  593. package/dist/orchestrator/service-router.js +157 -25
  594. package/dist/orchestrator/service-router.js.map +1 -1
  595. package/dist/orchestrator/topology-query.d.ts +49 -0
  596. package/dist/orchestrator/topology-query.d.ts.map +1 -0
  597. package/dist/orchestrator/topology-query.js +54 -0
  598. package/dist/orchestrator/topology-query.js.map +1 -0
  599. package/dist/project/artifact-builder.d.ts +174 -3
  600. package/dist/project/artifact-builder.d.ts.map +1 -1
  601. package/dist/project/artifact-builder.js +454 -42
  602. package/dist/project/artifact-builder.js.map +1 -1
  603. package/dist/project/build-decision.d.ts +58 -0
  604. package/dist/project/build-decision.d.ts.map +1 -0
  605. package/dist/project/build-decision.js +59 -0
  606. package/dist/project/build-decision.js.map +1 -0
  607. package/dist/project/config-resolver.d.ts +6 -4
  608. package/dist/project/config-resolver.d.ts.map +1 -1
  609. package/dist/project/config-resolver.js +128 -74
  610. package/dist/project/config-resolver.js.map +1 -1
  611. package/dist/project/declared-config.d.ts +42 -0
  612. package/dist/project/declared-config.d.ts.map +1 -0
  613. package/dist/project/declared-config.js +60 -0
  614. package/dist/project/declared-config.js.map +1 -0
  615. package/dist/project/dependency-analyzer.d.ts.map +1 -1
  616. package/dist/project/dependency-analyzer.js.map +1 -1
  617. package/dist/project/deploy-phases.d.ts +35 -0
  618. package/dist/project/deploy-phases.d.ts.map +1 -0
  619. package/dist/project/deploy-phases.js +75 -0
  620. package/dist/project/deploy-phases.js.map +1 -0
  621. package/dist/project/exec-in-group.d.ts +49 -0
  622. package/dist/project/exec-in-group.d.ts.map +1 -0
  623. package/dist/project/exec-in-group.js +100 -0
  624. package/dist/project/exec-in-group.js.map +1 -0
  625. package/dist/project/node-app-config.d.ts +101 -0
  626. package/dist/project/node-app-config.d.ts.map +1 -0
  627. package/dist/project/node-app-config.js +134 -0
  628. package/dist/project/node-app-config.js.map +1 -0
  629. package/dist/project/node-app-health.d.ts +76 -0
  630. package/dist/project/node-app-health.d.ts.map +1 -0
  631. package/dist/project/node-app-health.js +119 -0
  632. package/dist/project/node-app-health.js.map +1 -0
  633. package/dist/project/operator-account.d.ts +293 -0
  634. package/dist/project/operator-account.d.ts.map +1 -0
  635. package/dist/project/operator-account.js +373 -0
  636. package/dist/project/operator-account.js.map +1 -0
  637. package/dist/project/registry.d.ts +45 -8
  638. package/dist/project/registry.d.ts.map +1 -1
  639. package/dist/project/registry.js +214 -64
  640. package/dist/project/registry.js.map +1 -1
  641. package/dist/project/requirements-scanner.d.ts +1 -0
  642. package/dist/project/requirements-scanner.d.ts.map +1 -1
  643. package/dist/project/requirements-scanner.js +28 -3
  644. package/dist/project/requirements-scanner.js.map +1 -1
  645. package/dist/project/stack-env.d.ts +37 -0
  646. package/dist/project/stack-env.d.ts.map +1 -0
  647. package/dist/project/stack-env.js +85 -0
  648. package/dist/project/stack-env.js.map +1 -0
  649. package/dist/project/stand-containers.d.ts +11 -0
  650. package/dist/project/stand-containers.d.ts.map +1 -0
  651. package/dist/project/stand-containers.js +27 -0
  652. package/dist/project/stand-containers.js.map +1 -0
  653. package/dist/project/working-tree.d.ts +62 -0
  654. package/dist/project/working-tree.d.ts.map +1 -0
  655. package/dist/project/working-tree.js +101 -0
  656. package/dist/project/working-tree.js.map +1 -0
  657. package/dist/release/artifact-store.d.ts +360 -0
  658. package/dist/release/artifact-store.d.ts.map +1 -0
  659. package/dist/release/artifact-store.js +1040 -0
  660. package/dist/release/artifact-store.js.map +1 -0
  661. package/dist/release/attest-on-node.d.ts +93 -0
  662. package/dist/release/attest-on-node.d.ts.map +1 -0
  663. package/dist/release/attest-on-node.js +207 -0
  664. package/dist/release/attest-on-node.js.map +1 -0
  665. package/dist/release/attest.d.ts +135 -0
  666. package/dist/release/attest.d.ts.map +1 -0
  667. package/dist/release/attest.js +199 -0
  668. package/dist/release/attest.js.map +1 -0
  669. package/dist/release/build-run.d.ts +151 -0
  670. package/dist/release/build-run.d.ts.map +1 -0
  671. package/dist/release/build-run.js +609 -0
  672. package/dist/release/build-run.js.map +1 -0
  673. package/dist/release/builder.d.ts +57 -0
  674. package/dist/release/builder.d.ts.map +1 -0
  675. package/dist/release/builder.js +120 -0
  676. package/dist/release/builder.js.map +1 -0
  677. package/dist/release/daemon-omni.d.ts +41 -0
  678. package/dist/release/daemon-omni.d.ts.map +1 -0
  679. package/dist/release/daemon-omni.js +59 -0
  680. package/dist/release/daemon-omni.js.map +1 -0
  681. package/dist/release/definition-inputs.d.ts +34 -0
  682. package/dist/release/definition-inputs.d.ts.map +1 -0
  683. package/dist/release/definition-inputs.js +90 -0
  684. package/dist/release/definition-inputs.js.map +1 -0
  685. package/dist/release/delivered.d.ts +53 -0
  686. package/dist/release/delivered.d.ts.map +1 -0
  687. package/dist/release/delivered.js +81 -0
  688. package/dist/release/delivered.js.map +1 -0
  689. package/dist/release/layout.d.ts +61 -0
  690. package/dist/release/layout.d.ts.map +1 -0
  691. package/dist/release/layout.js +114 -0
  692. package/dist/release/layout.js.map +1 -0
  693. package/dist/release/load.d.ts +44 -0
  694. package/dist/release/load.d.ts.map +1 -0
  695. package/dist/release/load.js +84 -0
  696. package/dist/release/load.js.map +1 -0
  697. package/dist/release/manifest.d.ts +299 -0
  698. package/dist/release/manifest.d.ts.map +1 -0
  699. package/dist/release/manifest.js +176 -0
  700. package/dist/release/manifest.js.map +1 -0
  701. package/dist/release/migrations.d.ts +32 -0
  702. package/dist/release/migrations.d.ts.map +1 -0
  703. package/dist/release/migrations.js +51 -0
  704. package/dist/release/migrations.js.map +1 -0
  705. package/dist/release/publish.d.ts +71 -0
  706. package/dist/release/publish.d.ts.map +1 -0
  707. package/dist/release/publish.js +102 -0
  708. package/dist/release/publish.js.map +1 -0
  709. package/dist/release/store.d.ts +174 -0
  710. package/dist/release/store.d.ts.map +1 -0
  711. package/dist/release/store.js +257 -0
  712. package/dist/release/store.js.map +1 -0
  713. package/dist/services/alert.rpc-service.d.ts +11 -15
  714. package/dist/services/alert.rpc-service.d.ts.map +1 -1
  715. package/dist/services/alert.rpc-service.js +29 -14
  716. package/dist/services/alert.rpc-service.js.map +1 -1
  717. package/dist/services/alert.service.d.ts +96 -59
  718. package/dist/services/alert.service.d.ts.map +1 -1
  719. package/dist/services/alert.service.js +455 -70
  720. package/dist/services/alert.service.js.map +1 -1
  721. package/dist/services/anonymous-input.d.ts +26 -0
  722. package/dist/services/anonymous-input.d.ts.map +1 -0
  723. package/dist/services/anonymous-input.js +44 -0
  724. package/dist/services/anonymous-input.js.map +1 -0
  725. package/dist/services/audit.rpc-service.d.ts +27 -0
  726. package/dist/services/audit.rpc-service.d.ts.map +1 -0
  727. package/dist/services/audit.rpc-service.js +57 -0
  728. package/dist/services/audit.rpc-service.js.map +1 -0
  729. package/dist/services/audit.service.d.ts +165 -0
  730. package/dist/services/audit.service.d.ts.map +1 -0
  731. package/dist/services/audit.service.js +242 -0
  732. package/dist/services/audit.service.js.map +1 -0
  733. package/dist/services/auth-context.d.ts +21 -1
  734. package/dist/services/auth-context.d.ts.map +1 -1
  735. package/dist/services/auth-context.js +51 -5
  736. package/dist/services/auth-context.js.map +1 -1
  737. package/dist/services/auth.rpc-service.d.ts +0 -1
  738. package/dist/services/auth.rpc-service.d.ts.map +1 -1
  739. package/dist/services/auth.rpc-service.js +23 -9
  740. package/dist/services/auth.rpc-service.js.map +1 -1
  741. package/dist/services/auth.service.d.ts +47 -33
  742. package/dist/services/auth.service.d.ts.map +1 -1
  743. package/dist/services/auth.service.js +155 -23
  744. package/dist/services/auth.service.js.map +1 -1
  745. package/dist/services/backup-pipeline.d.ts +97 -0
  746. package/dist/services/backup-pipeline.d.ts.map +1 -0
  747. package/dist/services/backup-pipeline.js +286 -0
  748. package/dist/services/backup-pipeline.js.map +1 -0
  749. package/dist/services/backup-schedule.d.ts +69 -0
  750. package/dist/services/backup-schedule.d.ts.map +1 -0
  751. package/dist/services/backup-schedule.js +132 -0
  752. package/dist/services/backup-schedule.js.map +1 -0
  753. package/dist/services/backup.rpc-service.d.ts +31 -2
  754. package/dist/services/backup.rpc-service.d.ts.map +1 -1
  755. package/dist/services/backup.rpc-service.js +53 -0
  756. package/dist/services/backup.rpc-service.js.map +1 -1
  757. package/dist/services/backup.service.d.ts +458 -14
  758. package/dist/services/backup.service.d.ts.map +1 -1
  759. package/dist/services/backup.service.js +1368 -170
  760. package/dist/services/backup.service.js.map +1 -1
  761. package/dist/services/bundle-build-worker.d.ts +19 -0
  762. package/dist/services/bundle-build-worker.d.ts.map +1 -0
  763. package/dist/services/bundle-build-worker.js +40 -0
  764. package/dist/services/bundle-build-worker.js.map +1 -0
  765. package/dist/services/bundle-builder.d.ts +490 -0
  766. package/dist/services/bundle-builder.d.ts.map +1 -0
  767. package/dist/services/bundle-builder.js +1159 -0
  768. package/dist/services/bundle-builder.js.map +1 -0
  769. package/dist/services/bundle-worker-protocol.d.ts +19 -0
  770. package/dist/services/bundle-worker-protocol.d.ts.map +1 -0
  771. package/dist/services/bundle-worker-protocol.js +32 -0
  772. package/dist/services/bundle-worker-protocol.js.map +1 -0
  773. package/dist/services/deploy.rpc-service.d.ts +4 -1
  774. package/dist/services/deploy.rpc-service.d.ts.map +1 -1
  775. package/dist/services/deploy.rpc-service.js +10 -0
  776. package/dist/services/deploy.rpc-service.js.map +1 -1
  777. package/dist/services/deploy.service.d.ts +14 -24
  778. package/dist/services/deploy.service.d.ts.map +1 -1
  779. package/dist/services/deploy.service.js +46 -5
  780. package/dist/services/deploy.service.js.map +1 -1
  781. package/dist/services/discovery.rpc-service.d.ts +2 -1
  782. package/dist/services/discovery.rpc-service.d.ts.map +1 -1
  783. package/dist/services/discovery.rpc-service.js.map +1 -1
  784. package/dist/services/discovery.service.d.ts +32 -28
  785. package/dist/services/discovery.service.d.ts.map +1 -1
  786. package/dist/services/discovery.service.js +108 -133
  787. package/dist/services/discovery.service.js.map +1 -1
  788. package/dist/services/event-broadcaster.rpc-service.d.ts +36 -23
  789. package/dist/services/event-broadcaster.rpc-service.d.ts.map +1 -1
  790. package/dist/services/event-broadcaster.rpc-service.js +70 -32
  791. package/dist/services/event-broadcaster.rpc-service.js.map +1 -1
  792. package/dist/services/event-broadcaster.service.d.ts.map +1 -1
  793. package/dist/services/event-broadcaster.service.js.map +1 -1
  794. package/dist/services/fleet.rpc-service.d.ts +2 -1
  795. package/dist/services/fleet.rpc-service.d.ts.map +1 -1
  796. package/dist/services/fleet.rpc-service.js +3 -1
  797. package/dist/services/fleet.rpc-service.js.map +1 -1
  798. package/dist/services/fleet.service.d.ts +32 -34
  799. package/dist/services/fleet.service.d.ts.map +1 -1
  800. package/dist/services/fleet.service.js +66 -10
  801. package/dist/services/fleet.service.js.map +1 -1
  802. package/dist/services/health-check.rpc-service.d.ts +2 -1
  803. package/dist/services/health-check.rpc-service.d.ts.map +1 -1
  804. package/dist/services/health-check.rpc-service.js.map +1 -1
  805. package/dist/services/health-check.service.d.ts +50 -36
  806. package/dist/services/health-check.service.d.ts.map +1 -1
  807. package/dist/services/health-check.service.js +179 -182
  808. package/dist/services/health-check.service.js.map +1 -1
  809. package/dist/services/host-operation-barrier.d.ts +8 -0
  810. package/dist/services/host-operation-barrier.d.ts.map +1 -0
  811. package/dist/services/host-operation-barrier.js +44 -0
  812. package/dist/services/host-operation-barrier.js.map +1 -0
  813. package/dist/services/host-upgrade.d.ts +129 -0
  814. package/dist/services/host-upgrade.d.ts.map +1 -0
  815. package/dist/services/host-upgrade.js +352 -0
  816. package/dist/services/host-upgrade.js.map +1 -0
  817. package/dist/services/infrastructure.rpc-service.d.ts +312 -2
  818. package/dist/services/infrastructure.rpc-service.d.ts.map +1 -1
  819. package/dist/services/infrastructure.rpc-service.js +584 -11
  820. package/dist/services/infrastructure.rpc-service.js.map +1 -1
  821. package/dist/services/kubernetes.rpc-service.d.ts +2 -1
  822. package/dist/services/kubernetes.rpc-service.d.ts.map +1 -1
  823. package/dist/services/kubernetes.rpc-service.js.map +1 -1
  824. package/dist/services/kubernetes.service.d.ts +5 -39
  825. package/dist/services/kubernetes.service.d.ts.map +1 -1
  826. package/dist/services/kubernetes.service.js +29 -5
  827. package/dist/services/kubernetes.service.js.map +1 -1
  828. package/dist/services/local-bundle.d.ts +160 -0
  829. package/dist/services/local-bundle.d.ts.map +1 -0
  830. package/dist/services/local-bundle.js +242 -0
  831. package/dist/services/local-bundle.js.map +1 -0
  832. package/dist/services/lockout.d.ts +28 -0
  833. package/dist/services/lockout.d.ts.map +1 -0
  834. package/dist/services/lockout.js +41 -0
  835. package/dist/services/lockout.js.map +1 -0
  836. package/dist/services/log-collector.rpc-service.d.ts +16 -0
  837. package/dist/services/log-collector.rpc-service.d.ts.map +1 -1
  838. package/dist/services/log-collector.rpc-service.js +16 -0
  839. package/dist/services/log-collector.rpc-service.js.map +1 -1
  840. package/dist/services/log-collector.service.d.ts +172 -54
  841. package/dist/services/log-collector.service.d.ts.map +1 -1
  842. package/dist/services/log-collector.service.js +492 -102
  843. package/dist/services/log-collector.service.js.map +1 -1
  844. package/dist/services/log-retention.d.ts +50 -0
  845. package/dist/services/log-retention.d.ts.map +1 -0
  846. package/dist/services/log-retention.js +52 -0
  847. package/dist/services/log-retention.js.map +1 -0
  848. package/dist/services/maintenance-lock.d.ts +162 -0
  849. package/dist/services/maintenance-lock.d.ts.map +1 -0
  850. package/dist/services/maintenance-lock.js +316 -0
  851. package/dist/services/maintenance-lock.js.map +1 -0
  852. package/dist/services/master-address.d.ts +107 -0
  853. package/dist/services/master-address.d.ts.map +1 -0
  854. package/dist/services/master-address.js +154 -0
  855. package/dist/services/master-address.js.map +1 -0
  856. package/dist/services/migration-output.d.ts +16 -0
  857. package/dist/services/migration-output.d.ts.map +1 -0
  858. package/dist/services/migration-output.js +22 -0
  859. package/dist/services/migration-output.js.map +1 -0
  860. package/dist/services/netron-telemetry-transport.d.ts.map +1 -1
  861. package/dist/services/netron-telemetry-transport.js.map +1 -1
  862. package/dist/services/node-deploy-lease.d.ts +171 -0
  863. package/dist/services/node-deploy-lease.d.ts.map +1 -0
  864. package/dist/services/node-deploy-lease.js +460 -0
  865. package/dist/services/node-deploy-lease.js.map +1 -0
  866. package/dist/services/node-health.repository.d.ts +63 -4
  867. package/dist/services/node-health.repository.d.ts.map +1 -1
  868. package/dist/services/node-health.repository.js +108 -67
  869. package/dist/services/node-health.repository.js.map +1 -1
  870. package/dist/services/node-manager.rpc-service.d.ts +360 -10
  871. package/dist/services/node-manager.rpc-service.d.ts.map +1 -1
  872. package/dist/services/node-manager.rpc-service.js +754 -34
  873. package/dist/services/node-manager.rpc-service.js.map +1 -1
  874. package/dist/services/node-manager.service.d.ts +267 -95
  875. package/dist/services/node-manager.service.d.ts.map +1 -1
  876. package/dist/services/node-manager.service.js +805 -103
  877. package/dist/services/node-manager.service.js.map +1 -1
  878. package/dist/services/node-upgrade.d.ts +129 -0
  879. package/dist/services/node-upgrade.d.ts.map +1 -0
  880. package/dist/services/node-upgrade.js +161 -0
  881. package/dist/services/node-upgrade.js.map +1 -0
  882. package/dist/services/node-upgrade.service.d.ts +232 -0
  883. package/dist/services/node-upgrade.service.d.ts.map +1 -0
  884. package/dist/services/node-upgrade.service.js +468 -0
  885. package/dist/services/node-upgrade.service.js.map +1 -0
  886. package/dist/services/pipeline.rpc-service.d.ts +6 -1
  887. package/dist/services/pipeline.rpc-service.d.ts.map +1 -1
  888. package/dist/services/pipeline.rpc-service.js +9 -0
  889. package/dist/services/pipeline.rpc-service.js.map +1 -1
  890. package/dist/services/pipeline.service.d.ts +17 -46
  891. package/dist/services/pipeline.service.d.ts.map +1 -1
  892. package/dist/services/pipeline.service.js +84 -9
  893. package/dist/services/pipeline.service.js.map +1 -1
  894. package/dist/services/project.rpc-service.d.ts +140 -5
  895. package/dist/services/project.rpc-service.d.ts.map +1 -1
  896. package/dist/services/project.rpc-service.js +260 -22
  897. package/dist/services/project.rpc-service.js.map +1 -1
  898. package/dist/services/project.service.d.ts +687 -15
  899. package/dist/services/project.service.d.ts.map +1 -1
  900. package/dist/services/project.service.js +2846 -337
  901. package/dist/services/project.service.js.map +1 -1
  902. package/dist/services/redeploy-decision.d.ts +176 -0
  903. package/dist/services/redeploy-decision.d.ts.map +1 -0
  904. package/dist/services/redeploy-decision.js +142 -0
  905. package/dist/services/redeploy-decision.js.map +1 -0
  906. package/dist/services/release.rpc-service.d.ts +159 -0
  907. package/dist/services/release.rpc-service.d.ts.map +1 -0
  908. package/dist/services/release.rpc-service.js +395 -0
  909. package/dist/services/release.rpc-service.js.map +1 -0
  910. package/dist/services/release.service.d.ts +114 -0
  911. package/dist/services/release.service.d.ts.map +1 -0
  912. package/dist/services/release.service.js +208 -0
  913. package/dist/services/release.service.js.map +1 -0
  914. package/dist/services/remote-deployer.service.d.ts +580 -8
  915. package/dist/services/remote-deployer.service.d.ts.map +1 -1
  916. package/dist/services/remote-deployer.service.js +1786 -169
  917. package/dist/services/remote-deployer.service.js.map +1 -1
  918. package/dist/services/remote-ops.service.d.ts +51 -9
  919. package/dist/services/remote-ops.service.d.ts.map +1 -1
  920. package/dist/services/remote-ops.service.js +235 -39
  921. package/dist/services/remote-ops.service.js.map +1 -1
  922. package/dist/services/remote-provisioner.d.ts +124 -0
  923. package/dist/services/remote-provisioner.d.ts.map +1 -0
  924. package/dist/services/remote-provisioner.js +258 -0
  925. package/dist/services/remote-provisioner.js.map +1 -0
  926. package/dist/services/secrets.rpc-service.d.ts +51 -2
  927. package/dist/services/secrets.rpc-service.d.ts.map +1 -1
  928. package/dist/services/secrets.rpc-service.js +81 -2
  929. package/dist/services/secrets.rpc-service.js.map +1 -1
  930. package/dist/services/secrets.service.d.ts +108 -9
  931. package/dist/services/secrets.service.d.ts.map +1 -1
  932. package/dist/services/secrets.service.js +313 -48
  933. package/dist/services/secrets.service.js.map +1 -1
  934. package/dist/services/slave-storage.service.d.ts +107 -0
  935. package/dist/services/slave-storage.service.d.ts.map +1 -1
  936. package/dist/services/slave-storage.service.js +171 -2
  937. package/dist/services/slave-storage.service.js.map +1 -1
  938. package/dist/services/static-carry-forward.d.ts +110 -0
  939. package/dist/services/static-carry-forward.d.ts.map +1 -0
  940. package/dist/services/static-carry-forward.js +433 -0
  941. package/dist/services/static-carry-forward.js.map +1 -0
  942. package/dist/services/sync-policy.d.ts +167 -0
  943. package/dist/services/sync-policy.d.ts.map +1 -0
  944. package/dist/services/sync-policy.js +161 -0
  945. package/dist/services/sync-policy.js.map +1 -0
  946. package/dist/services/sync-summary.d.ts +39 -0
  947. package/dist/services/sync-summary.d.ts.map +1 -0
  948. package/dist/services/sync-summary.js +62 -0
  949. package/dist/services/sync-summary.js.map +1 -0
  950. package/dist/services/sync.rpc-service.d.ts +19 -7
  951. package/dist/services/sync.rpc-service.d.ts.map +1 -1
  952. package/dist/services/sync.rpc-service.js +36 -5
  953. package/dist/services/sync.rpc-service.js.map +1 -1
  954. package/dist/services/sync.service.d.ts +350 -18
  955. package/dist/services/sync.service.d.ts.map +1 -1
  956. package/dist/services/sync.service.js +864 -116
  957. package/dist/services/sync.service.js.map +1 -1
  958. package/dist/services/system-info.rpc-service.d.ts +2 -1
  959. package/dist/services/system-info.rpc-service.d.ts.map +1 -1
  960. package/dist/services/system-info.rpc-service.js.map +1 -1
  961. package/dist/services/system-info.service.d.ts +56 -68
  962. package/dist/services/system-info.service.d.ts.map +1 -1
  963. package/dist/services/system-info.service.js +240 -127
  964. package/dist/services/system-info.service.js.map +1 -1
  965. package/dist/services/telemetry.rpc-service.d.ts.map +1 -1
  966. package/dist/services/telemetry.rpc-service.js +9 -3
  967. package/dist/services/telemetry.rpc-service.js.map +1 -1
  968. package/dist/services/telemetry.service.d.ts +4 -2
  969. package/dist/services/telemetry.service.d.ts.map +1 -1
  970. package/dist/services/telemetry.service.js +4 -2
  971. package/dist/services/telemetry.service.js.map +1 -1
  972. package/dist/services/trace-collector.rpc-service.d.ts +2 -1
  973. package/dist/services/trace-collector.rpc-service.d.ts.map +1 -1
  974. package/dist/services/trace-collector.rpc-service.js.map +1 -1
  975. package/dist/services/trace-collector.service.d.ts +2 -41
  976. package/dist/services/trace-collector.service.d.ts.map +1 -1
  977. package/dist/services/trace-collector.service.js +27 -7
  978. package/dist/services/trace-collector.service.js.map +1 -1
  979. package/dist/services/wait-for-postgres.d.ts +28 -0
  980. package/dist/services/wait-for-postgres.d.ts.map +1 -0
  981. package/dist/services/wait-for-postgres.js +77 -0
  982. package/dist/services/wait-for-postgres.js.map +1 -0
  983. package/dist/shared/alert-expression.d.ts +60 -0
  984. package/dist/shared/alert-expression.d.ts.map +1 -0
  985. package/dist/shared/alert-expression.js +124 -0
  986. package/dist/shared/alert-expression.js.map +1 -0
  987. package/dist/shared/audit-outcome.d.ts +39 -0
  988. package/dist/shared/audit-outcome.d.ts.map +1 -0
  989. package/dist/shared/audit-outcome.js +45 -0
  990. package/dist/shared/audit-outcome.js.map +1 -0
  991. package/dist/shared/build-freshness.d.ts +59 -0
  992. package/dist/shared/build-freshness.d.ts.map +1 -0
  993. package/dist/shared/build-freshness.js +113 -0
  994. package/dist/shared/build-freshness.js.map +1 -0
  995. package/dist/shared/build-stamp.d.ts +26 -0
  996. package/dist/shared/build-stamp.d.ts.map +1 -0
  997. package/dist/shared/build-stamp.js +36 -0
  998. package/dist/shared/build-stamp.js.map +1 -0
  999. package/dist/shared/describe-error.d.ts +22 -0
  1000. package/dist/shared/describe-error.d.ts.map +1 -0
  1001. package/dist/shared/describe-error.js +39 -0
  1002. package/dist/shared/describe-error.js.map +1 -0
  1003. package/dist/shared/dto/alerts.d.ts +101 -0
  1004. package/dist/shared/dto/alerts.d.ts.map +1 -0
  1005. package/dist/shared/dto/alerts.js +9 -0
  1006. package/dist/shared/dto/alerts.js.map +1 -0
  1007. package/dist/shared/dto/auth.d.ts +49 -0
  1008. package/dist/shared/dto/auth.d.ts.map +1 -0
  1009. package/dist/shared/dto/auth.js +14 -0
  1010. package/dist/shared/dto/auth.js.map +1 -0
  1011. package/dist/shared/dto/backups.d.ts +16 -0
  1012. package/dist/shared/dto/backups.d.ts.map +1 -0
  1013. package/dist/shared/dto/backups.js +9 -0
  1014. package/dist/shared/dto/backups.js.map +1 -0
  1015. package/dist/shared/dto/deploy.d.ts +28 -0
  1016. package/dist/shared/dto/deploy.d.ts.map +1 -0
  1017. package/dist/shared/dto/deploy.js +8 -0
  1018. package/dist/shared/dto/deploy.js.map +1 -0
  1019. package/dist/shared/dto/discovery.d.ts +24 -0
  1020. package/dist/shared/dto/discovery.d.ts.map +1 -0
  1021. package/dist/shared/dto/discovery.js +9 -0
  1022. package/dist/shared/dto/discovery.js.map +1 -0
  1023. package/dist/shared/dto/fleet.d.ts +35 -0
  1024. package/dist/shared/dto/fleet.d.ts.map +1 -0
  1025. package/dist/shared/dto/fleet.js +8 -0
  1026. package/dist/shared/dto/fleet.js.map +1 -0
  1027. package/dist/shared/dto/health.d.ts +26 -0
  1028. package/dist/shared/dto/health.d.ts.map +1 -0
  1029. package/dist/shared/dto/health.js +9 -0
  1030. package/dist/shared/dto/health.js.map +1 -0
  1031. package/dist/shared/dto/kubernetes.d.ts +44 -0
  1032. package/dist/shared/dto/kubernetes.d.ts.map +1 -0
  1033. package/dist/shared/dto/kubernetes.js +9 -0
  1034. package/dist/shared/dto/kubernetes.js.map +1 -0
  1035. package/dist/shared/dto/logs.d.ts +77 -0
  1036. package/dist/shared/dto/logs.d.ts.map +1 -0
  1037. package/dist/shared/dto/logs.js +10 -0
  1038. package/dist/shared/dto/logs.js.map +1 -0
  1039. package/dist/shared/dto/metrics.d.ts +65 -0
  1040. package/dist/shared/dto/metrics.d.ts.map +1 -0
  1041. package/dist/shared/dto/metrics.js +17 -0
  1042. package/dist/shared/dto/metrics.js.map +1 -0
  1043. package/dist/shared/dto/nodes.d.ts +347 -0
  1044. package/dist/shared/dto/nodes.d.ts.map +1 -0
  1045. package/dist/shared/dto/nodes.js +8 -0
  1046. package/dist/shared/dto/nodes.js.map +1 -0
  1047. package/dist/shared/dto/pipelines.d.ts +53 -0
  1048. package/dist/shared/dto/pipelines.d.ts.map +1 -0
  1049. package/dist/shared/dto/pipelines.js +9 -0
  1050. package/dist/shared/dto/pipelines.js.map +1 -0
  1051. package/dist/shared/dto/project.d.ts +142 -8
  1052. package/dist/shared/dto/project.d.ts.map +1 -1
  1053. package/dist/shared/dto/services.d.ts +858 -7
  1054. package/dist/shared/dto/services.d.ts.map +1 -1
  1055. package/dist/shared/dto/system-info.d.ts +93 -0
  1056. package/dist/shared/dto/system-info.d.ts.map +1 -0
  1057. package/dist/shared/dto/system-info.js +9 -0
  1058. package/dist/shared/dto/system-info.js.map +1 -0
  1059. package/dist/shared/dto/traces.d.ts +49 -0
  1060. package/dist/shared/dto/traces.d.ts.map +1 -0
  1061. package/dist/shared/dto/traces.js +9 -0
  1062. package/dist/shared/dto/traces.js.map +1 -0
  1063. package/dist/shared/env-config.d.ts +74 -0
  1064. package/dist/shared/env-config.d.ts.map +1 -0
  1065. package/dist/shared/env-config.js +99 -0
  1066. package/dist/shared/env-config.js.map +1 -0
  1067. package/dist/shared/events.d.ts +45 -39
  1068. package/dist/shared/events.d.ts.map +1 -1
  1069. package/dist/shared/events.js +6 -0
  1070. package/dist/shared/events.js.map +1 -1
  1071. package/dist/shared/format.d.ts +1 -1
  1072. package/dist/shared/format.d.ts.map +1 -1
  1073. package/dist/shared/format.js +4 -0
  1074. package/dist/shared/format.js.map +1 -1
  1075. package/dist/shared/local-infra-host.d.ts +28 -0
  1076. package/dist/shared/local-infra-host.d.ts.map +1 -0
  1077. package/dist/shared/local-infra-host.js +28 -0
  1078. package/dist/shared/local-infra-host.js.map +1 -0
  1079. package/dist/shared/node-check.d.ts +37 -0
  1080. package/dist/shared/node-check.d.ts.map +1 -0
  1081. package/dist/shared/node-check.js +41 -0
  1082. package/dist/shared/node-check.js.map +1 -0
  1083. package/dist/shared/paths.d.ts +29 -0
  1084. package/dist/shared/paths.d.ts.map +1 -0
  1085. package/dist/shared/paths.js +54 -0
  1086. package/dist/shared/paths.js.map +1 -0
  1087. package/dist/shared/pnpm.d.ts +29 -0
  1088. package/dist/shared/pnpm.d.ts.map +1 -0
  1089. package/dist/shared/pnpm.js +57 -0
  1090. package/dist/shared/pnpm.js.map +1 -0
  1091. package/dist/shared/private-files.d.ts +29 -0
  1092. package/dist/shared/private-files.d.ts.map +1 -0
  1093. package/dist/shared/private-files.js +109 -0
  1094. package/dist/shared/private-files.js.map +1 -0
  1095. package/dist/shared/redact-env.d.ts +24 -0
  1096. package/dist/shared/redact-env.d.ts.map +1 -0
  1097. package/dist/shared/redact-env.js +36 -0
  1098. package/dist/shared/redact-env.js.map +1 -0
  1099. package/dist/shared/release-reading.d.ts +85 -0
  1100. package/dist/shared/release-reading.d.ts.map +1 -0
  1101. package/dist/shared/release-reading.js +81 -0
  1102. package/dist/shared/release-reading.js.map +1 -0
  1103. package/dist/shared/roles.d.ts +58 -3
  1104. package/dist/shared/roles.d.ts.map +1 -1
  1105. package/dist/shared/roles.js +55 -0
  1106. package/dist/shared/roles.js.map +1 -1
  1107. package/dist/shared/rpcauth.d.ts +13 -0
  1108. package/dist/shared/rpcauth.d.ts.map +1 -0
  1109. package/dist/shared/rpcauth.js +23 -0
  1110. package/dist/shared/rpcauth.js.map +1 -0
  1111. package/dist/shared/shell-escape.d.ts +9 -0
  1112. package/dist/shared/shell-escape.d.ts.map +1 -0
  1113. package/dist/shared/shell-escape.js +11 -0
  1114. package/dist/shared/shell-escape.js.map +1 -0
  1115. package/dist/shared/sync-reading.d.ts +45 -0
  1116. package/dist/shared/sync-reading.d.ts.map +1 -0
  1117. package/dist/shared/sync-reading.js +42 -0
  1118. package/dist/shared/sync-reading.js.map +1 -0
  1119. package/dist/shared/tokens.d.ts +19 -0
  1120. package/dist/shared/tokens.d.ts.map +1 -1
  1121. package/dist/shared/tokens.js +28 -0
  1122. package/dist/shared/tokens.js.map +1 -1
  1123. package/dist/webapp/webapp.service.d.ts +33 -2
  1124. package/dist/webapp/webapp.service.d.ts.map +1 -1
  1125. package/dist/webapp/webapp.service.js +143 -31
  1126. package/dist/webapp/webapp.service.js.map +1 -1
  1127. package/dist/workers/health-monitor-process.d.ts.map +1 -1
  1128. package/dist/workers/health-monitor-process.js +10 -0
  1129. package/dist/workers/health-monitor-process.js.map +1 -1
  1130. package/dist/workers/health-monitor.service.d.ts +45 -3
  1131. package/dist/workers/health-monitor.service.d.ts.map +1 -1
  1132. package/dist/workers/health-monitor.service.js +152 -25
  1133. package/dist/workers/health-monitor.service.js.map +1 -1
  1134. package/dist/workers/system-worker-manager.d.ts +43 -0
  1135. package/dist/workers/system-worker-manager.d.ts.map +1 -1
  1136. package/dist/workers/system-worker-manager.js +150 -3
  1137. package/dist/workers/system-worker-manager.js.map +1 -1
  1138. package/dist/workers/types.d.ts +8 -2
  1139. package/dist/workers/types.d.ts.map +1 -1
  1140. package/package.json +68 -30
  1141. package/webapp/dist/BUILD.json +5 -0
  1142. package/webapp/dist/assets/FormControlLabel-BbSxpQwN.js +1 -0
  1143. package/webapp/dist/assets/Grid-DizFZYue.js +1 -0
  1144. package/webapp/dist/assets/Switch-Bfiqzx8l.js +1 -0
  1145. package/webapp/dist/assets/alerts-DH_RVRS3.js +1 -0
  1146. package/webapp/dist/assets/apps-B96355sz.js +1 -0
  1147. package/webapp/dist/assets/audit-CYGtbi4k.js +1 -0
  1148. package/webapp/dist/assets/client-BmqFExWT.js +1 -0
  1149. package/webapp/dist/assets/constants-veKGFkC6.js +1 -0
  1150. package/webapp/dist/assets/containers-B_TpThln.js +2 -0
  1151. package/webapp/dist/assets/daemon-memory-CCPZ4cV_.js +1 -0
  1152. package/webapp/dist/assets/dashboard-DHf2_65N.js +1 -0
  1153. package/webapp/dist/assets/dashboard-builder-CWY2pjeG.js +1 -0
  1154. package/webapp/dist/assets/deploy-release-dialog-Dp-zpSAd.js +1 -0
  1155. package/webapp/dist/assets/deployments-tJI6O3fK.js +1 -0
  1156. package/webapp/dist/assets/detail-B2zP1Hto.js +8 -0
  1157. package/webapp/dist/assets/detail-CoIrwmsy.js +1 -0
  1158. package/webapp/dist/assets/detail-DWaGaOtL.js +2 -0
  1159. package/webapp/dist/assets/formatters-BqjL73N3.js +1 -0
  1160. package/webapp/dist/assets/index-Dvo0iprx.css +1 -0
  1161. package/webapp/dist/assets/index-vMnY_4jR.js +412 -0
  1162. package/webapp/dist/assets/logs-DTRjOfX8.js +12 -0
  1163. package/webapp/dist/assets/maplibre-gl-DowHoZAx.js +814 -0
  1164. package/webapp/dist/assets/metrics-UCajtdox.js +2 -0
  1165. package/webapp/dist/assets/nodes-DuTVU0f6.js +7 -0
  1166. package/webapp/dist/assets/pipelines-C8BeDcAl.js +4 -0
  1167. package/webapp/dist/assets/projects-Dp_zFfLY.js +1 -0
  1168. package/webapp/dist/assets/react-DlmbjZJV.js +1 -0
  1169. package/webapp/dist/assets/react-apexcharts.esm-C8pn45ml.js +97 -0
  1170. package/webapp/dist/assets/release-bits-BzTKz5fM.js +1 -0
  1171. package/webapp/dist/assets/release-build-panel-DcW9YcXQ.js +1 -0
  1172. package/webapp/dist/assets/releases-B4hcr1bE.js +2 -0
  1173. package/webapp/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
  1174. package/webapp/dist/assets/settings-DIteZTbe.js +1 -0
  1175. package/webapp/dist/assets/settled-pair-C7SjZuns.js +1 -0
  1176. package/webapp/dist/assets/sign-in-D4kMBOHp.js +1 -0
  1177. package/webapp/dist/assets/stacks-Brnf-EB7.js +1 -0
  1178. package/webapp/dist/assets/sync-reading-C3PnJ7AX.js +1 -0
  1179. package/webapp/dist/assets/system-info-CFDNVKkU.js +1 -0
  1180. package/webapp/dist/assets/topology-7WeTUsDN.js +12 -0
  1181. package/webapp/dist/assets/topology-DLioOiRN.css +1 -0
  1182. package/webapp/dist/assets/traces-9c3gwCN-.js +1 -0
  1183. package/webapp/dist/assets/use-stack-context-CnA5FTHu.js +1 -0
  1184. package/webapp/dist/index.html +6 -6
  1185. package/dist/commands/dev.d.ts +0 -23
  1186. package/dist/commands/dev.d.ts.map +0 -1
  1187. package/dist/commands/dev.js +0 -76
  1188. package/dist/commands/dev.js.map +0 -1
  1189. package/dist/config/define-app.d.ts +0 -6
  1190. package/dist/config/define-app.d.ts.map +0 -1
  1191. package/dist/config/define-app.js +0 -38
  1192. package/dist/config/define-app.js.map +0 -1
  1193. package/dist/infrastructure/gateway-generator.d.ts +0 -63
  1194. package/dist/infrastructure/gateway-generator.d.ts.map +0 -1
  1195. package/dist/infrastructure/gateway-generator.js +0 -253
  1196. package/dist/infrastructure/gateway-generator.js.map +0 -1
  1197. package/dist/monitoring/health.service.d.ts +0 -14
  1198. package/dist/monitoring/health.service.d.ts.map +0 -1
  1199. package/dist/monitoring/health.service.js +0 -50
  1200. package/dist/monitoring/health.service.js.map +0 -1
  1201. package/dist/monitoring/metrics.service.d.ts +0 -14
  1202. package/dist/monitoring/metrics.service.d.ts.map +0 -1
  1203. package/dist/monitoring/metrics.service.js +0 -40
  1204. package/dist/monitoring/metrics.service.js.map +0 -1
  1205. package/dist/orchestrator/bootstrap-runner.d.ts +0 -15
  1206. package/dist/orchestrator/bootstrap-runner.d.ts.map +0 -1
  1207. package/dist/orchestrator/bootstrap-runner.js +0 -152
  1208. package/dist/orchestrator/bootstrap-runner.js.map +0 -1
  1209. package/dist/orchestrator/topology-proxy.d.ts +0 -65
  1210. package/dist/orchestrator/topology-proxy.d.ts.map +0 -1
  1211. package/dist/orchestrator/topology-proxy.js +0 -209
  1212. package/dist/orchestrator/topology-proxy.js.map +0 -1
  1213. package/dist/orchestrator/topology-relay.d.ts +0 -111
  1214. package/dist/orchestrator/topology-relay.d.ts.map +0 -1
  1215. package/dist/orchestrator/topology-relay.js +0 -255
  1216. package/dist/orchestrator/topology-relay.js.map +0 -1
  1217. package/dist/services/auth-middleware.d.ts +0 -47
  1218. package/dist/services/auth-middleware.d.ts.map +0 -1
  1219. package/dist/services/auth-middleware.js +0 -158
  1220. package/dist/services/auth-middleware.js.map +0 -1
  1221. package/dist/services/environment.rpc-service.d.ts +0 -22
  1222. package/dist/services/environment.rpc-service.d.ts.map +0 -1
  1223. package/dist/services/environment.rpc-service.js +0 -65
  1224. package/dist/services/environment.rpc-service.js.map +0 -1
  1225. package/dist/services/environment.service.d.ts +0 -87
  1226. package/dist/services/environment.service.d.ts.map +0 -1
  1227. package/dist/services/environment.service.js +0 -220
  1228. package/dist/services/environment.service.js.map +0 -1
  1229. package/dist/services/metrics-collector.service.d.ts +0 -103
  1230. package/dist/services/metrics-collector.service.d.ts.map +0 -1
  1231. package/dist/services/metrics-collector.service.js +0 -235
  1232. package/dist/services/metrics-collector.service.js.map +0 -1
  1233. package/dist/services/metrics.rpc-service.d.ts +0 -41
  1234. package/dist/services/metrics.rpc-service.d.ts.map +0 -1
  1235. package/dist/services/metrics.rpc-service.js +0 -90
  1236. package/dist/services/metrics.rpc-service.js.map +0 -1
  1237. package/dist/services/metrics.service.d.ts +0 -72
  1238. package/dist/services/metrics.service.d.ts.map +0 -1
  1239. package/dist/services/metrics.service.js +0 -185
  1240. package/dist/services/metrics.service.js.map +0 -1
  1241. package/dist/supervisor/omnitron-supervisor.d.ts +0 -21
  1242. package/dist/supervisor/omnitron-supervisor.d.ts.map +0 -1
  1243. package/dist/supervisor/omnitron-supervisor.js +0 -43
  1244. package/dist/supervisor/omnitron-supervisor.js.map +0 -1
  1245. package/dist/webapp/static-server.d.ts +0 -24
  1246. package/dist/webapp/static-server.d.ts.map +0 -1
  1247. package/dist/webapp/static-server.js +0 -152
  1248. package/dist/webapp/static-server.js.map +0 -1
  1249. package/webapp/dist/assets/Badge-D2ZBP5iK.js +0 -1
  1250. package/webapp/dist/assets/Card-vO88g06B.js +0 -1
  1251. package/webapp/dist/assets/CardActions-C9h7Q2wW.js +0 -1
  1252. package/webapp/dist/assets/CardContent-BA_Xmmhk.js +0 -1
  1253. package/webapp/dist/assets/CardHeader-ceIs__6x.js +0 -1
  1254. package/webapp/dist/assets/Chip-Ckk0xUhK.js +0 -1
  1255. package/webapp/dist/assets/DialogActions-BhHM7hov.js +0 -1
  1256. package/webapp/dist/assets/DialogContentText-BT2JQRmK.js +0 -1
  1257. package/webapp/dist/assets/FormControlLabel-CcQByVS8.js +0 -1
  1258. package/webapp/dist/assets/Grid-14PETRQW.js +0 -1
  1259. package/webapp/dist/assets/InputAdornment-B8aHIsQ7.js +0 -1
  1260. package/webapp/dist/assets/Skeleton-B74-Ds6O.js +0 -37
  1261. package/webapp/dist/assets/Switch-DvWD2wh4.js +0 -1
  1262. package/webapp/dist/assets/SwitchBase-CDU4nY8O.js +0 -1
  1263. package/webapp/dist/assets/Tab-DcCOYYp5.js +0 -1
  1264. package/webapp/dist/assets/TableContainer-CzTh4mu-.js +0 -1
  1265. package/webapp/dist/assets/TableRow-BySEreZR.js +0 -2
  1266. package/webapp/dist/assets/TextField-BLf-BHz2.js +0 -1
  1267. package/webapp/dist/assets/alerts-DnRbT41C.js +0 -1
  1268. package/webapp/dist/assets/apps-CdR0VH2d.js +0 -1
  1269. package/webapp/dist/assets/chunk-DtcAfS_W.js +0 -1
  1270. package/webapp/dist/assets/client-DU8I_lDc.js +0 -2
  1271. package/webapp/dist/assets/constants-BgnT-Xdy.js +0 -1
  1272. package/webapp/dist/assets/containers-Dw-rRXV1.js +0 -1
  1273. package/webapp/dist/assets/dashboard-CzgB0uZz.js +0 -1
  1274. package/webapp/dist/assets/dashboard-builder-DsA0yg33.js +0 -1
  1275. package/webapp/dist/assets/deployments-DtQAoIcQ.js +0 -1
  1276. package/webapp/dist/assets/detail-BJR-G7JG.js +0 -1
  1277. package/webapp/dist/assets/detail-nlCeSltl.js +0 -8
  1278. package/webapp/dist/assets/formatters-Do41SRmE.js +0 -1
  1279. package/webapp/dist/assets/index-ByQDwp2r.js +0 -566
  1280. package/webapp/dist/assets/index-CHVXOsTS.css +0 -1
  1281. package/webapp/dist/assets/isMuiElement-DwF2naaF.js +0 -1
  1282. package/webapp/dist/assets/logs-Kn8o4H7k.js +0 -11
  1283. package/webapp/dist/assets/metrics-DN8Zu5wT.js +0 -2
  1284. package/webapp/dist/assets/nodes-Bgch7AtN.js +0 -7
  1285. package/webapp/dist/assets/pipelines-Cry3qm-S.js +0 -4
  1286. package/webapp/dist/assets/popper-BJFSj6rc.js +0 -1
  1287. package/webapp/dist/assets/preload-helper-DSXbuxSR.js +0 -1
  1288. package/webapp/dist/assets/projects-Dym2GI1U.js +0 -1
  1289. package/webapp/dist/assets/react-apexcharts.esm-CSGK5Cxn.js +0 -958
  1290. package/webapp/dist/assets/react-apexcharts.esm-DEAWOwx5.js +0 -1003
  1291. package/webapp/dist/assets/settings-va3zTcow.js +0 -1
  1292. package/webapp/dist/assets/sign-in-ik35umbp.js +0 -1
  1293. package/webapp/dist/assets/stacks-CVFOnxbF.js +0 -1
  1294. package/webapp/dist/assets/system-info-npIXAjX2.js +0 -1
  1295. package/webapp/dist/assets/topology-CHpVij2M.css +0 -1
  1296. package/webapp/dist/assets/topology-bDe70vLs.js +0 -12
  1297. package/webapp/dist/assets/traces-DIIcT8DF.js +0 -1
  1298. package/webapp/dist/assets/use-stack-context-DHLwZv1r.js +0 -1
  1299. /package/webapp/dist/assets/{fs-CgZLtHiQ.js → fs-Ccl3sAcp.js} +0 -0
@@ -17,6 +17,7 @@
17
17
  * - RBAC: role-based access on all RPC endpoints (admin/operator/viewer)
18
18
  * - Config resolution: secrets + env-aware injection into apps
19
19
  */
20
+ import path from 'node:path';
20
21
  import { Application, ShutdownReason, ShutdownPriority, ApplicationEvent } from '@omnitron-dev/titan';
21
22
  import { UnixSocketTransport } from '@omnitron-dev/titan/netron/transport/unix';
22
23
  import { TcpTransport } from '@omnitron-dev/titan/netron/transport/tcp';
@@ -43,23 +44,191 @@ import { FileWatcher } from '../orchestrator/file-watcher.js';
43
44
  import { InfrastructureService } from '../infrastructure/infrastructure.service.js';
44
45
  import { DockerHealthIndicator } from '../monitoring/docker-health.indicator.js';
45
46
  import { AppHealthIndicator } from '../monitoring/app-health.indicator.js';
47
+ import { EventLoopWatch } from '../monitoring/event-loop-watch.js';
48
+ import { EventLoopStallIndicator } from '../monitoring/event-loop-stall.indicator.js';
46
49
  import { registerDaemonJobs } from './daemon-scheduler.js';
47
50
  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';
51
+ 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
52
  import { APP_EVENTS, INFRA_EVENTS, DAEMON_EVENTS, PROJECT_EVENTS, STACK_EVENTS, NODE_EVENTS, } from '../shared/events.js';
50
53
  import { JWT_SERVICE_TOKEN } from '@omnitron-dev/titan-auth';
51
- import { AuthenticationManager } from '@omnitron-dev/titan/netron/auth';
54
+ import { AuthenticationManager, AuthorizationManager, } from '@omnitron-dev/titan/netron/auth';
52
55
  import { createAuthContextWrapper } from '../services/auth-context.js';
56
+ import { ROLES } from '../shared/roles.js';
57
+ import { expandPath } from '../shared/paths.js';
58
+ /**
59
+ * Deadline for a request on the daemon's unix socket.
60
+ *
61
+ * Same value as `CLI_REQUEST_TIMEOUT` in `daemon-client.ts`: one number for
62
+ * one socket, stated by both ends rather than inherited from a default meant
63
+ * for wire requests.
64
+ */
65
+ const DAEMON_SOCKET_REQUEST_TIMEOUT = 60_000;
66
+ /**
67
+ * Where the daemon listens. The default is loopback; anything the operator
68
+ * writes is passed through literally, `0.0.0.0` included.
69
+ *
70
+ * Exported so the two call sites below and the tests agree on one
71
+ * definition. They previously each carried their own copy of
72
+ * `host !== '0.0.0.0' ? host : '127.0.0.1'`, which is how the two of them
73
+ * plus the config default managed to disagree about what the default was.
74
+ */
75
+ export function resolveBindHost(configured) {
76
+ return configured ?? '127.0.0.1';
77
+ }
78
+ /**
79
+ * Where a daemon binds its fleet TCP transport.
80
+ *
81
+ * A master follows `daemon.host`, whose default is loopback — the safe answer
82
+ * for the machine an operator is sitting at.
83
+ *
84
+ * A slave cannot use that default, because being reachable is the whole of
85
+ * what a slave is for: the master dials it to check its health and to pull
86
+ * from it. Bound to loopback it is a daemon nobody can see, and the console
87
+ * shows it as offline for ever with no way to tell it from a machine that is
88
+ * genuinely down.
89
+ *
90
+ * It was exactly that, and by a longer route than a wrong default. The
91
+ * generated slave config carried `daemon: { host: '0.0.0.0', … }` — and
92
+ * `IEcosystemConfig`, the schema of the file it was written into, has no
93
+ * `daemon` key; the daemon boots from `~/.omnitron/config.json`, which until
94
+ * now could not carry one either. So the setting was written, ignored, and
95
+ * every provisioned slave would have bound 127.0.0.1.
96
+ *
97
+ * An explicit `host` still wins, for an operator who wants one interface.
98
+ */
99
+ export function fleetBindHostFor(dc) {
100
+ if (dc.role === 'slave')
101
+ return dc.host ?? '0.0.0.0';
102
+ return resolveBindHost(dc.host);
103
+ }
104
+ /**
105
+ * The bind address a daemon should END UP with, from what was actually
106
+ * configured.
107
+ *
108
+ * `fleetBindHostFor` above takes the daemon's RUNNING config, and by the time
109
+ * anything holds one, `host` has been filled in from `DEFAULT_DAEMON_CONFIG`
110
+ * — so `?? '0.0.0.0'` can never fire and every slave binds loopback. The
111
+ * information it needs is whether an operator SET a host, and only the saved
112
+ * file knows that: absent means "no opinion", `127.0.0.1` means "loopback,
113
+ * deliberately".
114
+ *
115
+ * Measured 2026-09-14 on a provisioned slave, after the rule was written and
116
+ * before this existed:
117
+ *
118
+ * LISTEN 127.0.0.1:9700 ← the fleet port no master can dial
119
+ * LISTEN 127.0.0.1:9801
120
+ * LISTEN 127.0.0.1:9802
121
+ *
122
+ * The rule was right, the value it was given was already decided. This is the
123
+ * same rule applied where the answer is still open.
124
+ */
125
+ export function effectiveBindHost(saved) {
126
+ if (saved?.host)
127
+ return saved.host;
128
+ if (saved?.role === 'slave')
129
+ return '0.0.0.0';
130
+ return DEFAULT_DAEMON_CONFIG.host;
131
+ }
132
+ /**
133
+ * Where a daemon binds the surfaces that exist to serve a console: the Netron
134
+ * HTTP RPC and the WebSocket.
135
+ *
136
+ * A master follows `daemon.host`, like everything else. A slave does not,
137
+ * because on a slave that knob is published for a different reason: a fleet
138
+ * node has to bind its TCP transport somewhere the master can reach, and a
139
+ * generated slave config therefore says `0.0.0.0`. The same value then
140
+ * published two more surfaces — an authentication endpoint and a WebSocket
141
+ * one that has no rate limit available to it — and a slave serves no console,
142
+ * so nothing dials either. They were cost without benefit, on a machine whose
143
+ * whole point is being somewhere else.
144
+ */
145
+ export function consoleBindHostFor(dc) {
146
+ return dc.role === 'slave' ? '127.0.0.1' : resolveBindHost(dc.host);
147
+ }
148
+ /** Addresses that reach this host and nowhere else. */
149
+ export function isLoopbackHost(host) {
150
+ if (!host)
151
+ return true; // absent means the loopback default above
152
+ const h = host.trim().toLowerCase();
153
+ return h === '127.0.0.1' || h === 'localhost' || h === '::1' || h === '[::1]' || h.startsWith('127.');
154
+ }
155
+ /**
156
+ * Is the daemon still accepting the password its own migration seeded?
157
+ *
158
+ * Resolves the auth service lazily and answers `false` on any failure — a
159
+ * daemon that cannot reach its database has a louder problem than this, and
160
+ * a warning that fails the boot would be a worse trade than the risk it
161
+ * describes.
162
+ */
163
+ async function isSeededAdminPasswordInUse(app, logger) {
164
+ try {
165
+ const auth = await app.container.resolveAsync(AUTH_SERVICE_TOKEN);
166
+ return await auth.isUsingSeededAdminPassword();
167
+ }
168
+ catch (err) {
169
+ logger.warn({ err: err.message }, 'Could not check whether the seeded admin password is still in use');
170
+ return false;
171
+ }
172
+ }
173
+ /**
174
+ * The address other fleet nodes should dial to reach this one.
175
+ *
176
+ * `peer.address` is not decoration: `leader-election.ts` calls `requestVote`
177
+ * and `leaderHeartbeat` against it. A node bound to `0.0.0.0` therefore may
178
+ * not advertise `0.0.0.0` (not routable) — and must not advertise
179
+ * `127.0.0.1` either, which is what it did: every other node in the fleet
180
+ * resolves that to itself, so votes and heartbeats meant for the leader go
181
+ * to the sender's own loopback. A wrong answer of the right shape.
182
+ *
183
+ * When the bind address says nothing about reachability, take the first
184
+ * non-internal IPv4 the host has. Loopback remains the honest answer only
185
+ * when there is genuinely nothing else — a single-host deployment, where
186
+ * no other node can reach this one regardless.
187
+ */
188
+ export function advertisedAddress(bindHost) {
189
+ if (bindHost && bindHost !== '0.0.0.0' && bindHost !== '::')
190
+ return bindHost;
191
+ for (const addrs of Object.values(os.networkInterfaces())) {
192
+ for (const a of addrs ?? []) {
193
+ if (a.family === 'IPv4' && !a.internal)
194
+ return a.address;
195
+ }
196
+ }
197
+ return '127.0.0.1';
198
+ }
53
199
  export class OmnitronDaemon {
54
200
  app = null;
55
201
  pidManager = null;
202
+ /** Port this daemon serves `/netron/invoke` on; advertised to fleet peers. */
203
+ rpcHttpPort = null;
56
204
  fileWatcher = null;
57
205
  infraService = null;
206
+ eventLoopWatch = null;
58
207
  eventBroadcaster = null;
59
208
  syncService = null;
60
209
  nodeManagerService = null;
210
+ slaveConnector = null;
211
+ meshHandle = null;
61
212
  nodeManagerRpcService = null;
213
+ /**
214
+ * The live record of release builds this daemon is running.
215
+ *
216
+ * Held on the daemon rather than in the container because it is the state
217
+ * of work in progress: a build that a restart kills leaves a release
218
+ * directory with no manifest, and that — not this — is the durable trace.
219
+ */
220
+ releaseService = null;
62
221
  systemWorkerManager = null;
222
+ /** Everything the health-monitor worker needs to be spawned again. */
223
+ healthWorkerSpawn = null;
224
+ healthWorkerRestartTimer = null;
225
+ healthWorkerRestartAttempts = 0;
226
+ /** The startup budget the worker is given, so a failure can name it. */
227
+ healthWorkerStartupTimeout = 0;
228
+ /** In-daemon check timer, armed only while the worker is down. */
229
+ fallbackNodeCheckTimer = null;
230
+ /** Set by the shutdown task so nothing schedules work on the way out. */
231
+ isShuttingDown = false;
63
232
  leaderElection = null;
64
233
  configSyncService = null;
65
234
  metricsServer = null;
@@ -69,12 +238,17 @@ export class OmnitronDaemon {
69
238
  get daemonConfig() { return this.dc; }
70
239
  async start(config, options, daemonConfig) {
71
240
  this.dc = daemonConfig ?? DEFAULT_DAEMON_CONFIG;
72
- const pidFile = this.dc.pidFile.replace('~', process.env['HOME'] ?? '');
241
+ const pidFile = expandPath(this.dc.pidFile);
73
242
  this.pidManager = new PidManager(pidFile);
74
243
  if (this.pidManager.isRunning()) {
75
244
  const existingPid = this.pidManager.getPid();
76
245
  throw new Error(`Omnitron daemon already running (PID: ${existingPid})`);
77
246
  }
247
+ // Stale file from a previous crash is normal — clean it up so the
248
+ // atomic `O_EXCL` write below succeeds. P1-L: doing this AFTER
249
+ // isRunning() returned false means we only delete known-dead
250
+ // markers; live daemons get the conflict error above.
251
+ this.pidManager.remove();
78
252
  this.pidManager.write();
79
253
  // 1. Create Titan Application (the daemon itself)
80
254
  const DaemonModule = createDaemonModule(config, this.dc);
@@ -82,6 +256,25 @@ export class OmnitronDaemon {
82
256
  name: 'omnitron',
83
257
  version: CLI_VERSION,
84
258
  });
259
+ // P1-M — install our own SIGHUP handler BEFORE Application's
260
+ // default shutdown handlers attach, so `kill -HUP <daemon>`
261
+ // triggers a config reload (the Unix convention for long-running
262
+ // daemons) instead of terminating the supervisor. We don't bind
263
+ // this through the Application's signal manager because that
264
+ // path is wired to `runShutdownTasks()`; we install at the
265
+ // process level and the listener runs first.
266
+ process.on('SIGHUP', () => {
267
+ this.reloadConfigOnHup().catch((err) => {
268
+ // The message already carries which steps applied — printing only
269
+ // `.message` was fine for the load failure and misleading for the
270
+ // apply failure, which is the one that leaves work half-done.
271
+ console.warn(`[omnitron] SIGHUP config reload failed: ${err.message}`);
272
+ const cause = err.cause;
273
+ if (cause) {
274
+ console.warn(`[omnitron] caused by: ${cause.stack ?? String(cause)}`);
275
+ }
276
+ });
277
+ });
85
278
  // 2. Register transports (Unix, TCP, HTTP, WebSocket)
86
279
  await this.registerTransports(this.dc);
87
280
  // 3. Resolve and expose RPC services via Netron
@@ -90,6 +283,12 @@ export class OmnitronDaemon {
90
283
  await this.app.start();
91
284
  const loggerModule = await this.app.container.resolveAsync(LOGGER_SERVICE_TOKEN);
92
285
  const logger = loggerModule.logger;
286
+ // As soon as there is somewhere to say it, so a stall in the boot's own
287
+ // steps — infrastructure, migrations — is seen too. A node whose loop
288
+ // stands still answers nothing, its heartbeat included, and until this
289
+ // nothing on the node could say so. See `monitoring/event-loop-watch.ts`.
290
+ this.eventLoopWatch = new EventLoopWatch(logger);
291
+ this.eventLoopWatch.start();
93
292
  logger.info({
94
293
  socketPath: this.dc.socketPath,
95
294
  port: this.dc.port,
@@ -108,12 +307,12 @@ export class OmnitronDaemon {
108
307
  // 7. Register custom health indicators (Docker + apps)
109
308
  await this.registerHealthIndicators();
110
309
  // 8. File watching (dev mode)
111
- this.startFileWatcher(config, options, logger);
310
+ await this.startFileWatcher(config, options, logger);
112
311
  // 8.5. Wire daemon Netron to orchestrator for native topology routing
113
312
  {
114
313
  const orchestrator = await this.app.container.resolveAsync(ORCHESTRATOR_TOKEN);
115
314
  if (this.app.netron) {
116
- const socketPath = this.dc.socketPath.replace('~', process.env['HOME'] ?? '');
315
+ const socketPath = expandPath(this.dc.socketPath);
117
316
  orchestrator.setDaemonNetron(this.app.netron, socketPath);
118
317
  }
119
318
  // 8.6. Wire metrics bridge + start /metrics HTTP endpoint.
@@ -148,7 +347,24 @@ export class OmnitronDaemon {
148
347
  if (savedConfig?.webapp) {
149
348
  try {
150
349
  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);
350
+ // Publishing the console while the seeded password is still in
351
+ // place hands the whole control plane — start, stop, restart, logs,
352
+ // config — to anyone who can reach the port and guess a word that
353
+ // is written in the migration. Migration 001 says the password
354
+ // "MUST be changed on first login"; nothing enforced it, and a
355
+ // comment is not a control.
356
+ //
357
+ // So the console falls back to loopback rather than refusing to
358
+ // start: the operator needs it to change the password, and a
359
+ // control plane that will not come up is its own outage. The log
360
+ // line says what to do and that this is not the configured bind.
361
+ let consoleHost = this.dc.consoleBindHost ?? '127.0.0.1';
362
+ if (!isLoopbackHost(consoleHost) && (await isSeededAdminPasswordInUse(this.app, logger))) {
363
+ logger.error({ configured: consoleHost, bound: '127.0.0.1' }, 'REFUSING to publish the Console while the seeded admin password is unchanged — ' +
364
+ 'binding loopback instead. Sign in at http://127.0.0.1 and change it, then restart.');
365
+ consoleHost = '127.0.0.1';
366
+ }
367
+ const webapp = new WebappService(logger, process.cwd(), (this.dc.httpPort ?? 9800) + 1, this.dc.httpPort ?? 9800, consoleHost);
152
368
  await webapp.start();
153
369
  }
154
370
  catch (err) {
@@ -156,14 +372,34 @@ export class OmnitronDaemon {
156
372
  }
157
373
  }
158
374
  }
375
+ // 8.9. Fleet monitoring. Started BEFORE managed apps and not awaited.
376
+ //
377
+ // It used to be step 11.5, after `startApps`, `startScheduledTasks` and
378
+ // `startBackgroundServices`. Fleet monitoring is about MACHINES; it has
379
+ // nothing to do with whether this daemon's managed applications start, and
380
+ // gating it behind them means one application that fails its startup
381
+ // leaves the whole fleet unwatched for as long as the supervisor keeps
382
+ // retrying it. Measured on this host: ten minutes after a daemon start,
383
+ // with two applications still cycling through failed starts, not a single
384
+ // node had been checked and nothing had said so.
385
+ //
386
+ // Not awaited, for the same reason in the other direction: spawning the
387
+ // worker has a 30-second startup timeout, and the applications should not
388
+ // wait behind it. The function handles and logs its own failures, and arms
389
+ // the in-daemon fallback checks when the worker cannot run.
390
+ void this.startHealthMonitorWorker(logger).catch((err) => {
391
+ // It handles its own failures; this is the last resort, because an
392
+ // unhandled rejection is a crash of the daemon rather than a lost
393
+ // subsystem.
394
+ logger.error({ error: err.message }, 'Health monitor startup threw');
395
+ });
159
396
  // 9. Start managed apps (waits for infra gate if apps have `requires`)
160
397
  await this.startApps(config, logger);
161
398
  // 10. Start background tasks via titan-scheduler (replaces bare setInterval)
162
399
  await this.startScheduledTasks(config, logger);
163
400
  // 11. Start remaining background services (telemetry, traces, leader election)
164
401
  await this.startBackgroundServices(config, logger);
165
- // 11.5. Start health monitor system worker (master only)
166
- await this.startHealthMonitorWorker(logger);
402
+ // (Fleet monitoring is started at 8.5, above — before managed apps.)
167
403
  // 12. Wire event broadcasting (orchestrator events → WebSocket push)
168
404
  await this.wireEventBroadcasting(logger);
169
405
  // 13. Register shutdown tasks — Application handles SIGTERM/SIGINT
@@ -186,18 +422,87 @@ export class OmnitronDaemon {
186
422
  this.app.netron.registerTransportServer('unix', {
187
423
  name: 'daemon-local',
188
424
  options: {
189
- path: dc.socketPath.replace('~', process.env['HOME'] ?? ''),
425
+ path: expandPath(dc.socketPath),
190
426
  force: true,
191
427
  mode: 0o600, // Owner-only access for security
428
+ // Every other party on this socket states a deadline and this one
429
+ // did not, so `netron.ts:797` — `serverRequestTimeout ??
430
+ // transportOpts.requestTimeout` — found both undefined and built its
431
+ // accepted-connection peers with netron's 5 s default, which is a
432
+ // deadline for a wire request rather than for management-plane work.
433
+ // The reading of the server option was added deliberately («a
434
+ // `requestTimeout` set on the server was silently ignored»); the
435
+ // value was never supplied. Same number as `CLI_REQUEST_TIMEOUT`,
436
+ // which the daemon's own client uses over this same socket.
437
+ requestTimeout: DAEMON_SOCKET_REQUEST_TIMEOUT,
438
+ // The same bridge the HTTP and WebSocket servers below have: it puts
439
+ // the context the guard admitted a call with — here the implicit
440
+ // `omnitron-local` set just after this — where the services read
441
+ // who is calling. This server had none, so for every CLI call the
442
+ // guard saw `omnitron-local` and the services saw nobody: the audit
443
+ // trail recorded `system` for 171 of the master's 178 rows, and an
444
+ // operator's deployment read exactly like the daemon's own boot
445
+ // autostart. No proxy stands in front of a unix socket, so the
446
+ // wrapper trusts no forwarded address — its default.
447
+ invocationWrapper: createAuthContextWrapper(),
192
448
  },
193
449
  });
194
- // TCP — remote fleet communication (cross-server)
450
+ // Local CLI trust. The Unix socket above is created mode 0600, so any
451
+ // peer that can open it is provably the same OS user that owns this
452
+ // daemon — the OS has already authenticated them. Grant those
453
+ // connections an implicit admin context so the local CLI satisfies the
454
+ // `@Public({ auth: { roles } })` guards on the daemon RPC services
455
+ // without a redundant JWT handshake (the CLI talks over this socket
456
+ // only). Remote transports — TCP (fleet), HTTP/WS (webapp) — are NOT
457
+ // registered here and keep requiring a validated token, so this widens
458
+ // nothing beyond same-user local access that the socket mode already
459
+ // grants.
460
+ this.app.netron.setTransportAuthContext('unix', {
461
+ userId: 'omnitron-local',
462
+ roles: [ROLES.ADMIN],
463
+ permissions: [],
464
+ metadata: { source: 'local-unix-socket' },
465
+ });
466
+ // TCP — remote fleet communication (cross-server). The default is
467
+ // 127.0.0.1 so a single-host deployment never exposes daemon control
468
+ // to the LAN; multi-host fleet operators opt in via `daemon.host`.
469
+ //
470
+ // The default belongs in DEFAULT_DAEMON_CONFIG, not here. It used to be
471
+ // enforced here instead, as `dc.host !== '0.0.0.0' ? dc.host :
472
+ // '127.0.0.1'` over a config default of `'0.0.0.0'` — absence encoded as
473
+ // a value from the domain of valid answers. `0.0.0.0` is not a marker for
474
+ // "unset", it is a bind address with a meaning, and it was the exact
475
+ // value the comment above it told fleet operators to set. Following the
476
+ // documentation produced loopback and no fleet, silently. It also
477
+ // contradicted every neighbouring knob: `consoleBindHost: '0.0.0.0'`
478
+ // publishes, and so does an app's transport host.
479
+ //
480
+ // (The original hazard — pre-fix this defaulted to all-interfaces while
481
+ // the AuthorizationManager below was missing, giving any LAN host an
482
+ // unauthenticated `stopAll({force:true})` — is answered by the default
483
+ // being loopback and by that manager now being wired.)
484
+ const tcpHost = fleetBindHostFor(dc);
485
+ if (!isLoopbackHost(tcpHost)) {
486
+ // Said, not enforced, and the difference is deliberate: silently
487
+ // rebinding a fleet transport to loopback would take a cluster down,
488
+ // and a node that cannot be reached is an outage rather than a
489
+ // hardening. The console above IS rebound, because its whole purpose
490
+ // is a password prompt and loopback still serves it.
491
+ void (async () => {
492
+ const loggerModule = await this.app.container.resolveAsync(LOGGER_SERVICE_TOKEN);
493
+ const log = loggerModule.logger;
494
+ if (await isSeededAdminPasswordInUse(this.app, log)) {
495
+ log.error({ host: tcpHost, port: dc.port }, 'The daemon fleet transport is published and the seeded admin password is unchanged — ' +
496
+ 'anyone who can reach this port can sign in and stop every app. Change it now.');
497
+ }
498
+ })().catch(() => undefined);
499
+ }
195
500
  this.app.netron.registerTransport('tcp', () => new TcpTransport());
196
501
  this.app.netron.registerTransportServer('tcp', {
197
502
  name: 'daemon-fleet',
198
503
  options: {
199
504
  port: dc.port,
200
- host: dc.host,
505
+ host: tcpHost,
201
506
  },
202
507
  });
203
508
  // Auth — JWT validation + session check + role extraction
@@ -246,18 +551,79 @@ export class OmnitronDaemon {
246
551
  },
247
552
  tokenCache: { enabled: true, ttl: 10_000 },
248
553
  });
249
- this.app.netron.configureAuth(authManager);
250
- const authContextWrapper = createAuthContextWrapper();
554
+ // Wire the AuthorizationManager so the `@Public({ auth: { roles } })`
555
+ // decorators on DaemonRpcService are actually enforced on the wire.
556
+ // Without an authzManager, `remote-peer.ts:enforceMethodAccess`
557
+ // early-returns and every method is reachable by anyone who can
558
+ // mint a valid JWT, regardless of `roles`. The WS origin policy
559
+ // (`allowedOrigins: true` → same-host) closes the cross-origin
560
+ // upgrade attack against cookie-mode auth.
561
+ const authzManager = new AuthorizationManager(authLogger);
562
+ this.app.netron.configureAuth(authManager, authzManager, {
563
+ allowedOrigins: true,
564
+ });
565
+ // `trustProxy` is shared with the rate limiter deliberately: both answer
566
+ // the same question — is there a proxy in front whose forwarded headers
567
+ // we may believe? Two separate switches would eventually disagree.
568
+ const authContextWrapper = createAuthContextWrapper({
569
+ trustProxy: dc.httpRateLimit?.trustProxy === true,
570
+ });
571
+ // HTTP / WS bind also defaults to 127.0.0.1. The webapp talks to
572
+ // these over the same loopback; nginx fronts the public surface.
573
+ // Operators who proxy in from another host opt in via `daemon.host`
574
+ // (same knob that controls TCP, with the same literal meaning).
575
+ //
576
+ // Except on a slave, where that knob has to be published for a different
577
+ // reason and publishing these with it is pure cost. A fleet node must
578
+ // bind its TCP transport somewhere the master can reach — that is what
579
+ // `daemon.host: '0.0.0.0'` in a generated slave config is for — and the
580
+ // same value then published the Netron HTTP RPC and the WebSocket
581
+ // alongside it. A slave serves no console: the operator uses the
582
+ // master's. So the two surfaces it gained are an authentication endpoint
583
+ // it does not need and a WebSocket one that, as the note below records,
584
+ // has no rate limit available to it.
585
+ //
586
+ // Rebound rather than warned about, which is the opposite of the choice
587
+ // made for the TCP transport twenty lines up — and for the opposite
588
+ // reason. Rebinding the fleet port would take a cluster down, because
589
+ // being reachable is that port's whole job. Rebinding these costs a slave
590
+ // nothing, because nothing dials them.
591
+ const localHost = consoleBindHostFor(dc);
251
592
  // HTTP — Netron RPC API (internal port, nginx proxies from public port)
593
+ // The public `httpPort` (9800) is nginx in front of the console; the
594
+ // daemon's own Netron HTTP listener sits one above it. Remembered here
595
+ // because the fleet registration below must advertise the port peers can
596
+ // actually POST `/netron/invoke` to — not the TCP port, and not the
597
+ // console's.
252
598
  const internalHttpPort = (dc.httpPort ?? 9800) + 1;
599
+ this.rpcHttpPort = internalHttpPort;
253
600
  this.app.netron.registerTransport('http', () => new HttpTransport());
601
+ const rl = dc.httpRateLimit ?? {};
254
602
  this.app.netron.registerTransportServer('http', {
255
603
  name: 'daemon-http',
256
604
  options: {
257
605
  port: internalHttpPort,
258
- host: '0.0.0.0',
606
+ host: localHost,
259
607
  cors: true,
260
608
  invocationWrapper: authContextWrapper,
609
+ // Volume cap on the HTTP RPC surface, and only that one. Password
610
+ // guessing is bounded by the per-account lockout in AuthService.
611
+ //
612
+ // The WebSocket surface below is NOT covered: netron's WebSocket
613
+ // transport has no rate-limit option — `maxPayload` is the only
614
+ // bound it accepts — and the console talks over WebSocket. This
615
+ // comment used to say the limit bounded "everything else, including
616
+ // attempts to flood the daemon into unresponsiveness", which
617
+ // describes a protection that covers one of the two transports an
618
+ // attacker can reach.
619
+ rateLimit: {
620
+ enabled: rl.enabled ?? true,
621
+ windowMs: rl.windowMs ?? 60_000,
622
+ maxRequests: rl.maxRequests ?? 3_000,
623
+ globalMaxRequests: rl.globalMaxRequests ?? 6_000,
624
+ trustProxy: rl.trustProxy ?? false,
625
+ ...(rl.whitelist ? { whitelist: rl.whitelist } : {}),
626
+ },
261
627
  },
262
628
  });
263
629
  // WebSocket — real-time event push to webapp clients
@@ -267,8 +633,18 @@ export class OmnitronDaemon {
267
633
  name: 'daemon-ws',
268
634
  options: {
269
635
  port: wsPort,
270
- host: '0.0.0.0',
636
+ host: localHost,
637
+ // Applied per invocation by `remote-peer.ts`, the dispatcher every
638
+ // socket transport shares — so WS calls get the same request context
639
+ // HTTP calls do. Until recently only the HTTP server read this
640
+ // option and this line did nothing.
271
641
  invocationWrapper: authContextWrapper,
642
+ // The only bound this transport accepts. Left unset it inherits
643
+ // `ws`'s 100 MB default, which is a lot of memory to hand a single
644
+ // frame on a control-plane socket that carries RPC arguments and
645
+ // event pushes. 8 MB is well above anything the console sends and
646
+ // well below anything worth allocating for a stranger.
647
+ maxPayload: dc.wsMaxPayload ?? 8 * 1024 * 1024,
272
648
  },
273
649
  });
274
650
  }
@@ -286,11 +662,20 @@ export class OmnitronDaemon {
286
662
  const logManager = await container.resolveAsync(LOG_MANAGER_TOKEN);
287
663
  const logCollector = await container.resolveAsync(LOG_COLLECTOR_TOKEN);
288
664
  const loggerModule = await container.resolveAsync(LOGGER_SERVICE_TOKEN);
665
+ // Table retention. `logging.maxSize` and `maxFiles` bound the rotated
666
+ // files on disk and say nothing about the `logs` table, which had no
667
+ // bound at all — 13 GB on the development host. Off unless configured,
668
+ // and the number comes from the config rather than a default chosen
669
+ // inside the collector, because a wrong default there deletes history.
670
+ logCollector.setRetentionDays(config.logging?.databaseRetentionDays ?? 0, loggerModule.logger.child({ component: 'log-retention' }));
289
671
  const rpcService = new DaemonRpcService(orchestrator, titanHealth, logManager, config, this);
290
672
  await this.app.netron.peer.exposeService(rpcService);
291
673
  // Logs RPC service (works on both master and slave — uses PG or SQLite)
292
674
  const logsRpcService = new LogsRpcService(logCollector);
293
675
  await this.app.netron.peer.exposeService(logsRpcService);
676
+ // `getLogs` answers an app this daemon does not run from the stored rows —
677
+ // where a node's synced logs are.
678
+ rpcService.setLogStore(logCollector);
294
679
  // Metrics RPC service — provided by TitanMetricsModule
295
680
  const metricsService = await container.resolveAsync(TITAN_METRICS_TOKEN);
296
681
  const { MetricsRpcService: MetricsRpc } = await import('@omnitron-dev/titan-metrics');
@@ -301,10 +686,29 @@ export class OmnitronDaemon {
301
686
  const healthCheckService = await container.resolveAsync(HEALTH_CHECK_SERVICE_TOKEN);
302
687
  const healthCheckRpcService = new HealthCheckRpc(healthCheckService);
303
688
  await this.app.netron.peer.exposeService(healthCheckRpcService);
689
+ // The audit trail. Master-only — its table lives in the omnitron
690
+ // database — and every service that records into it takes it as an
691
+ // optional dependency, so a slave serves the same methods and records
692
+ // nothing rather than refusing them.
693
+ let audit;
694
+ if (!isSlave) {
695
+ try {
696
+ audit = await container.resolveAsync(AUDIT_SERVICE_TOKEN);
697
+ // Revealing an app's secrets is recorded (`revealEnv`).
698
+ rpcService.setAudit(audit);
699
+ const { AuditRpcService } = await import('../services/audit.rpc-service.js');
700
+ await this.app.netron.peer.exposeService(new AuditRpcService(audit));
701
+ }
702
+ catch (err) {
703
+ // A daemon whose database did not come up still runs; it simply
704
+ // records nothing, and says so once rather than at every action.
705
+ loggerModule.logger.warn({ error: err.message }, 'No audit trail on this daemon — actions will not be recorded');
706
+ }
707
+ }
304
708
  // Secrets RPC service (file-based, no DB dependency)
305
709
  const { SecretsRpcService: SecretsRpc } = await import('../services/secrets.rpc-service.js');
306
710
  const secretsService = await container.resolveAsync(SECRETS_SERVICE_TOKEN);
307
- const secretsRpcService = new SecretsRpc(secretsService);
711
+ const secretsRpcService = new SecretsRpc(secretsService, audit);
308
712
  await this.app.netron.peer.exposeService(secretsRpcService);
309
713
  // Kubernetes RPC service (no DB dependency)
310
714
  const { KubernetesRpcService: K8sRpc } = await import('../services/kubernetes.rpc-service.js');
@@ -316,14 +720,60 @@ export class OmnitronDaemon {
316
720
  const backupService = await container.resolveAsync(BACKUP_SERVICE_TOKEN);
317
721
  const backupRpcService = new BackupRpc(backupService);
318
722
  await this.app.netron.peer.exposeService(backupRpcService);
723
+ // Re-arm any persisted backup schedules so they survive daemon restarts.
724
+ await backupService.restoreSchedules().catch((err) => loggerModule.logger.warn({ err: err.message }, 'Failed to restore backup schedules'));
319
725
  // Infrastructure RPC service (reports infra state — slave has no infra)
320
726
  const { InfrastructureRpcService: InfraRpc } = await import('../services/infrastructure.rpc-service.js');
321
- const infraRpcService = new InfraRpc(() => this.infraService);
727
+ const infraRpcService = new InfraRpc(() => this.infraService,
728
+ // Only a node hosts a stack's infrastructure on demand. A master
729
+ // provisions its own from its own config at boot, and handing it a
730
+ // second one over RPC would give one machine two reconcilers over one
731
+ // set of containers.
732
+ isSlave
733
+ ? (infraConfig, declaredServices, presetRegistry, serviceOverrides) => {
734
+ const service = new InfrastructureService(loggerModule.logger.child({ component: 'infra' }), infraConfig, declaredServices, presetRegistry, serviceOverrides,
735
+ // A node keeps its own state in SQLite. Provisioning a
736
+ // control-plane Postgres for it gives the host a database
737
+ // nobody queries, on default credentials.
738
+ false);
739
+ this.infraService = service;
740
+ return service;
741
+ }
742
+ : undefined,
743
+ // The node's own vault. Generated service credentials are written
744
+ // here and read back on every later provision, which is what makes a
745
+ // generated password usable: a data directory keeps the one it was
746
+ // initialised with.
747
+ // Resolved per call, not captured here: a resolution that fails during
748
+ // startup would otherwise leave credential generation off for the life
749
+ // of this daemon, and the absence reads as "this node needs none".
750
+ isSlave
751
+ ? () => {
752
+ try {
753
+ return container.resolve(SECRETS_SERVICE_TOKEN);
754
+ }
755
+ catch {
756
+ return undefined;
757
+ }
758
+ }
759
+ : undefined,
760
+ // The logger, because the one thing this service says on its own — that
761
+ // a deployment is running on a default credential — is said at error
762
+ // level and reaches nobody without it.
763
+ loggerModule.logger.child({ component: 'infra' }));
322
764
  await this.app.netron.peer.exposeService(infraRpcService);
323
765
  // Project + Stack management RPC service
324
766
  const projectService = await container.resolveAsync(PROJECT_SERVICE_TOKEN);
325
- const projectRpcService = new ProjectRpcService(projectService);
767
+ const projectRpcService = new ProjectRpcService(projectService, audit);
326
768
  await this.app.netron.peer.exposeService(projectRpcService);
769
+ // Releases. Master-only, like the builds themselves: a release is built
770
+ // from clean clones of BOTH repositories, and only a master has both.
771
+ if (!isSlave) {
772
+ const { ReleaseService } = await import('../services/release.service.js');
773
+ const { ReleaseRpcService } = await import('../services/release.rpc-service.js');
774
+ this.releaseService = new ReleaseService(loggerModule.logger.child({ component: 'release' }));
775
+ await this.app.netron.peer.exposeService(new ReleaseRpcService(this.releaseService, audit, projectService));
776
+ }
327
777
  // Sync service (slave→master data replication)
328
778
  const syncNodeId = `${os.hostname()}-${dc.port}`;
329
779
  let syncDb;
@@ -335,6 +785,53 @@ export class OmnitronDaemon {
335
785
  syncDb = await container.resolveAsync(OMNITRON_DB_TOKEN);
336
786
  }
337
787
  this.syncService = new SyncService(syncDb, loggerModule.logger.child({ component: 'sync' }), syncNodeId, dc.role, dc.sync);
788
+ // Remote metrics into the store the console actually reads.
789
+ //
790
+ // `ingestMetric` wrote them to `metrics_raw`, and nothing in this
791
+ // repository reads that table — the metrics page queries titan-metrics'
792
+ // own storage. So once the pipeline carried anything, it would have
793
+ // carried it somewhere invisible. Recording through the same service the
794
+ // master uses for its own readings makes a remote node a LABEL on the
795
+ // existing series instead of a second store of the same concept.
796
+ //
797
+ // `node` is a distinct argument in the sink's signature rather than a
798
+ // key a caller may forget to put in `labels`: a sample recorded without
799
+ // it merges with the master's own, and the chart then shows two machines
800
+ // summed under one name.
801
+ if (!isSlave) {
802
+ const syncMetrics = await container.resolveAsync(TITAN_METRICS_TOKEN);
803
+ this.syncService.setMetricsSink((sample) => {
804
+ try {
805
+ syncMetrics.recordTyped('gauge', sample.name, { ...sample.labels, app: sample.app, node: sample.node }, sample.value);
806
+ }
807
+ catch (err) {
808
+ loggerModule.logger.warn({ node: sample.node, name: sample.name, error: err.message }, 'Failed to record a replicated metric');
809
+ }
810
+ });
811
+ }
812
+ // The producer the replication pipeline never had.
813
+ //
814
+ // `SyncService` buffers, batches, retries with backoff, evicts when over
815
+ // budget, and the master keeps a dedup ledger for what it has taken —
816
+ // all of it built, and `buffer`/`bufferBatch` had ZERO callers. A slave
817
+ // collected its logs into its own SQLite and replicated none of them,
818
+ // and nothing said so: an empty buffer drains successfully every cycle.
819
+ //
820
+ // Wired only for a slave. `bufferBatch` is a no-op for any other role,
821
+ // so this is belt and braces rather than the guard itself — but a master
822
+ // has no master to ship to, and doing the work would be pure cost.
823
+ if (isSlave) {
824
+ const syncService = this.syncService;
825
+ logCollector.setSyncSink((entries) => {
826
+ void syncService
827
+ .bufferBatch(entries.map((e) => ({ category: 'logs', payload: e })))
828
+ .catch(() => {
829
+ // `bufferBatch` logs its own failure. Swallowed here because the
830
+ // lines are already in the local table: losing the replica copy
831
+ // must not cost the flush that succeeded.
832
+ });
833
+ });
834
+ }
338
835
  const syncRpcService = new SyncRpcService(this.syncService);
339
836
  await this.app.netron.peer.exposeService(syncRpcService);
340
837
  // System info service (real-time OS/hardware metrics)
@@ -343,19 +840,120 @@ export class OmnitronDaemon {
343
840
  await this.app.netron.peer.exposeService(systemInfoRpcService);
344
841
  // Event Broadcaster RPC service (WebSocket subscriptions)
345
842
  this.eventBroadcaster = new EventBroadcasterService(loggerModule.logger);
346
- const eventBroadcasterRpcService = new EventBroadcasterRpcService(this.eventBroadcaster);
843
+ const eventBroadcasterRpcService = new EventBroadcasterRpcService(this.eventBroadcaster, this.app.netron);
347
844
  await this.app.netron.peer.exposeService(eventBroadcasterRpcService);
348
845
  // Node manager — file-based, no PG (master only — manages infrastructure nodes)
349
846
  // Health checks delegated to dedicated system worker (started later after infra is ready)
350
847
  if (!isSlave) {
351
848
  const { NodeManagerService } = await import('../services/node-manager.service.js');
352
849
  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);
850
+ const { DaemonStateStore: DaemonStateStoreCls } = await import('./daemon-state-store.service.js');
851
+ const daemonStateStore = await container.resolveAsync(DAEMON_STATE_STORE_TOKEN);
852
+ const nodeManager = new NodeManagerService(loggerModule.logger.child({ component: 'nodes' }), daemonStateStore, secretsService);
355
853
  this.nodeManagerService = nodeManager;
356
- const nodeManagerRpcService = new NodeManagerRpcService(nodeManager);
854
+ const nodeManagerRpcService = new NodeManagerRpcService(nodeManager, audit);
855
+ // The deployer reaches nodes through the same SSH implementation as the
856
+ // health checks — the only one that can present a stored password or a
857
+ // key passphrase.
858
+ {
859
+ const { RemoteDeployer } = await import('../services/remote-deployer.service.js');
860
+ const { ExecutionService } = await import('../execution/execution.service.js');
861
+ const deployLogger = loggerModule.logger.child({ component: 'deploy' });
862
+ const remoteDeployer = new RemoteDeployer(deployLogger, new ExecutionService(deployLogger));
863
+ nodeManagerRpcService.setRemoteDeployer(remoteDeployer);
864
+ // Upgrading a node from the console: the same build the CLI makes,
865
+ // made here, then the same two calls. Without this the console could
866
+ // see every node and update none of them.
867
+ const { NodeUpgradeService } = await import('../services/node-upgrade.service.js');
868
+ nodeManagerRpcService.setUpgradeService(new NodeUpgradeService(loggerModule.logger.child({ component: 'node-upgrade' }), {
869
+ getNode: (id) => nodeManager.getNode(id),
870
+ // The same projection the CLI builds for `fleet upgrade`, made
871
+ // here so the console's plan and the terminal's dry run decide
872
+ // from identical inputs. Two places computing «what is this
873
+ // node running» is how they come to disagree.
874
+ listCandidates: () => nodeManager.listNodes().map((n) => ({
875
+ nodeId: n.id,
876
+ name: n.name,
877
+ currentVersion: n.status?.omnitronVersion ?? null,
878
+ isLocal: n.isLocal,
879
+ // SSH, not the daemon port: an upgrade travels over SSH,
880
+ // and a node whose daemon is unreachable is often exactly
881
+ // the one worth upgrading.
882
+ sshReachable: n.status?.sshConnected ?? null,
883
+ // The machine, for telling two registry names for one box
884
+ // apart before both get the bundle.
885
+ address: `${n.host}:${n.sshPort}`,
886
+ })),
887
+ }, (id) => nodeManager.nodeToDeployTarget(id), () => remoteDeployer, audit));
888
+ }
357
889
  this.nodeManagerRpcService = nodeManagerRpcService;
890
+ // The local node's own answers, for the three readers that ask every
891
+ // node about itself. A daemon has no mesh connection to itself, so
892
+ // `askNode` needs a local source for each.
893
+ //
894
+ // On the INSTANCE, after it is assigned, and not on `this.…?.` from
895
+ // wherever each dependency happens to be constructed: `setSyncService`
896
+ // was first written next to `new SyncService(...)`, ninety lines above
897
+ // this assignment and inside the same function, where `?.` swallowed it
898
+ // silently and the local node would have reported "not wired" forever.
899
+ // An optional call is only safe where the object is known to exist.
900
+ nodeManagerRpcService.setSyncService(this.syncService);
358
901
  await this.app.netron.peer.exposeService(nodeManagerRpcService);
902
+ // The mesh: every registered node, connected and replicating.
903
+ //
904
+ // `SlaveConnector` maintains the connections and pulls each node's
905
+ // write-ahead buffer on connect and on every heartbeat, and all of it
906
+ // worked — but the only caller of `addSlave` was a remote or cluster
907
+ // stack starting. So the master connected to the nodes something had
908
+ // been deployed onto, and to no others. A node added through the
909
+ // console, provisioned, and collecting its own metrics and logs was
910
+ // never dialled, and buffered locally forever: measured at 47,407
911
+ // undelivered entries over eleven hours on the first such node, which
912
+ // reported healthy the whole time.
913
+ //
914
+ // Built here rather than inside ProjectService because reaching a node
915
+ // needs what only this side has — the registry's SSH credentials, for
916
+ // the token the node's daemon demands and for the tunnel a firewalled
917
+ // daemon port needs. ProjectService adopts the same instance: one per
918
+ // master, or two connectors pull the same buffer and race to ack it.
919
+ try {
920
+ const { SlaveConnector } = await import('../cluster/slave-connector.js');
921
+ const { createMeshDialer } = await import('../cluster/mesh-link.js');
922
+ const { startMesh } = await import('../cluster/mesh.js');
923
+ const { ExecutionService: Exec } = await import('../execution/execution.service.js');
924
+ const meshLogger = loggerModule.logger.child({ component: 'mesh' });
925
+ const connector = new SlaveConnector(meshLogger, this.syncService ?? null, {
926
+ dial: createMeshDialer({
927
+ logger: meshLogger,
928
+ execution: new Exec(meshLogger),
929
+ subject: `mesh:${os.hostname()}`,
930
+ sshTargetFor: async ({ host }) => {
931
+ const node = nodeManager.listNodes().find((n) => n.host === host && !n.isLocal);
932
+ return node ? nodeManager.nodeToSshTarget(node) : null;
933
+ },
934
+ }),
935
+ });
936
+ this.slaveConnector = connector;
937
+ projectService.setSlaveConnector(connector);
938
+ // So the console can answer "is this node replicating", which is a
939
+ // different question from "can this master reach it".
940
+ nodeManagerRpcService.setSlaveConnector(connector);
941
+ // A stack names a host; the registry holds the credential for it.
942
+ // Wired here because this is the side that has both — ProjectService
943
+ // reads config files, the node registry keeps its secrets in the
944
+ // daemon's vault.
945
+ projectService.setNodeCredentialResolver(async (host) => {
946
+ const node = nodeManager.listNodes().find((n) => n.host === host && !n.isLocal);
947
+ return node ? nodeManager.nodeToSshTarget(node) : null;
948
+ });
949
+ this.meshHandle = startMesh({ registry: nodeManager, connector, logger: meshLogger });
950
+ }
951
+ catch (err) {
952
+ // A master with no mesh still supervises its own apps, so this does
953
+ // not stop the daemon — but nothing else reports it, and a silent
954
+ // failure here looks exactly like a fleet that has nothing to say.
955
+ loggerModule.logger.error({ error: err.message }, 'Mesh not started — registered nodes will buffer their data locally and replicate nothing');
956
+ }
359
957
  }
360
958
  // =====================================================================
361
959
  // Master-only RPC services (require PostgreSQL)
@@ -374,6 +972,9 @@ export class OmnitronDaemon {
374
972
  const { TelemetryRpcService: TelemetryRpc } = await import('../services/telemetry.rpc-service.js');
375
973
  const telemetryRelay = await container.resolveAsync(TELEMETRY_RELAY_TOKEN);
376
974
  const telemetryRpcService = new TelemetryRpc(telemetryRelay);
975
+ // The console asks every node for its relay, and a daemon has no mesh
976
+ // connection to itself, so the local node's answer comes from here.
977
+ this.nodeManagerRpcService?.setTelemetryRelay(telemetryRelay);
377
978
  await this.app.netron.peer.exposeService(telemetryRpcService);
378
979
  // Fleet management RPC service
379
980
  const { FleetRpcService: FleetRpc } = await import('../services/fleet.rpc-service.js');
@@ -413,6 +1014,8 @@ export class OmnitronDaemon {
413
1014
  ...(dc.cluster.heartbeatInterval != null && { heartbeatInterval: dc.cluster.heartbeatInterval }),
414
1015
  });
415
1016
  const clusterRpcService = new ClusterRpc(this.leaderElection);
1017
+ // The local node's own election state, for the cross-node reader.
1018
+ this.nodeManagerRpcService?.setLeaderElection(this.leaderElection);
416
1019
  await this.app.netron.peer.exposeService(clusterRpcService);
417
1020
  }
418
1021
  }
@@ -448,6 +1051,10 @@ export class OmnitronDaemon {
448
1051
  if (!this.app)
449
1052
  return;
450
1053
  const stateStore = await this.app.container.resolveAsync(STATE_STORE_TOKEN);
1054
+ // First-touch init reads from SQLite (and migrates the legacy
1055
+ // JSON snapshot in-place if one exists). Subsequent reads via
1056
+ // `load()` are synchronous from the in-memory cache.
1057
+ await stateStore.init();
451
1058
  const previousState = stateStore.load();
452
1059
  if (previousState) {
453
1060
  const runningApps = previousState.apps.filter((a) => a.status === 'online' || a.status === 'starting');
@@ -531,7 +1138,7 @@ export class OmnitronDaemon {
531
1138
  // stack auto-detection upstream); the daemon does not provision it
532
1139
  // again here.
533
1140
  const { ProjectRegistry } = await import('../project/registry.js');
534
- const projects = new ProjectRegistry().list();
1141
+ const projects = ProjectRegistry.open().list();
535
1142
  const projectModeActive = projects.some((p) => Array.isArray(p.enabledStacks) && p.enabledStacks.length > 0);
536
1143
  if (projectModeActive) {
537
1144
  logger.info({ projects: projects.length }, 'project mode active — daemon-level infrastructure deferred to stack provision');
@@ -590,27 +1197,70 @@ export class OmnitronDaemon {
590
1197
  */
591
1198
  async reconcileOrphanContainers(logger, projects, projectService) {
592
1199
  const { listManagedContainers, stopContainer, removeContainer } = await import('../infrastructure/container-runtime.js');
1200
+ const { decideOrphans } = await import('../infrastructure/orphan-containers.js');
593
1201
  const managed = await listManagedContainers();
594
1202
  if (managed.length === 0)
595
1203
  return;
596
1204
  // Build set of expected container name prefixes from registered projects/stacks
597
1205
  const expectedPrefixes = new Set();
598
1206
  const internalNames = new Set(['omnitron-pg', 'omnitron-nginx']);
1207
+ // Load each project's config FIRST. `listStacks` reads the loaded-config
1208
+ // cache and returns [] for a project whose config has not been read —
1209
+ // and this runs before the per-project load loop further down, whose own
1210
+ // comment says loading "populates internal cache used by listStacks".
1211
+ //
1212
+ // So on every boot where projects come from the registry rather than CWD
1213
+ // auto-detection — the normal case — `expectedPrefixes` was EMPTY and
1214
+ // every managed container looked like an orphan. Measured from this
1215
+ // daemon's own log, 2026-09-12 12:46, ten RUNNING containers removed in
1216
+ // fourteen seconds: postgres, redis, bitcoin, monero (daemon and wallet),
1217
+ // nominatim, tor, tiles, minio, gateway — each logged as "not part of any
1218
+ // registered stack" while being part of the registered stack.
1219
+ //
1220
+ // Named volumes survived (`docker rm -f`, no `-v`), so this cost a cold
1221
+ // restart of every service rather than the chain data. That is luck, not
1222
+ // design.
1223
+ let expectationsComplete = true;
599
1224
  for (const project of projects) {
1225
+ try {
1226
+ await projectService.loadProjectConfig(project.name);
1227
+ }
1228
+ catch (err) {
1229
+ expectationsComplete = false;
1230
+ logger.warn({ project: project.name, error: err.message }, 'Could not read this project’s stacks — skipping orphan reconciliation rather than guessing');
1231
+ continue;
1232
+ }
600
1233
  const stacks = projectService.listStacks(project.name);
1234
+ if (stacks.length === 0)
1235
+ expectationsComplete = false;
601
1236
  for (const stack of stacks) {
602
1237
  expectedPrefixes.add(`${project.name}-${stack.name}-`);
603
1238
  }
604
1239
  }
1240
+ // Whether anything may be removed is one decision with several ways of
1241
+ // being wrong, and every way it has been wrong so far was a mistake about
1242
+ // what the inputs MEANT. It lives in `decideOrphans`, where it can be
1243
+ // pinned in a test rather than discovered on a host.
1244
+ const decision = decideOrphans({
1245
+ isSlave: this.dc?.role === 'slave',
1246
+ managed: managed.map((c) => ({
1247
+ name: c.name,
1248
+ status: c.status,
1249
+ project: c.project,
1250
+ stack: c.stack,
1251
+ })),
1252
+ projects: projects.map((p) => p.name),
1253
+ expectedPrefixes: [...expectedPrefixes],
1254
+ expectationsComplete,
1255
+ internalNames: [...internalNames],
1256
+ });
1257
+ if (decision.action === 'skip') {
1258
+ logger.warn({ managed: managed.length, expectedPrefixes: expectedPrefixes.size, because: decision.because }, 'Orphan reconciliation skipped');
1259
+ return;
1260
+ }
605
1261
  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
1262
+ for (const name of decision.containers) {
1263
+ const container = managed.find((c) => c.name === name);
614
1264
  logger.info({ container: container.name, status: container.status }, 'Removing orphan container (not part of any registered stack)');
615
1265
  try {
616
1266
  if (container.status === 'running')
@@ -626,49 +1276,23 @@ export class OmnitronDaemon {
626
1276
  logger.info({ removed: orphanCount }, 'Orphan container cleanup complete');
627
1277
  }
628
1278
  }
1279
+ /**
1280
+ * Apply the internal-database schema.
1281
+ *
1282
+ * Delegates to the shared runner (`database/migration-runner.ts`) so this
1283
+ * path, the infrastructure service and the `migrate` CLI all execute the
1284
+ * same registry through the same engine. A failure here is logged as an
1285
+ * ERROR — the daemon keeps running, but every feature backed by
1286
+ * omnitron-pg (alerts, sessions, logs, metrics, deployments) is degraded
1287
+ * and the operator has to see it.
1288
+ */
629
1289
  async runOmnitronMigrations(logger) {
1290
+ const { runOmnitronMigrations } = await import('../database/migration-runner.js');
630
1291
  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
- }
1292
+ await runOmnitronMigrations(logger);
669
1293
  }
670
1294
  catch (err) {
671
- logger.warn({ error: err.message }, 'Could not run migrations — will retry on next start');
1295
+ logger.error({ error: err.message }, 'Omnitron database migrations FAILED — alerts, sessions, logs and metrics will not work until this is resolved');
672
1296
  }
673
1297
  }
674
1298
  // ============================================================================
@@ -684,6 +1308,15 @@ export class OmnitronDaemon {
684
1308
  titanHealth.registerIndicator(new DockerHealthIndicator(() => this.infraService));
685
1309
  // Managed apps health indicator
686
1310
  titanHealth.registerIndicator(new AppHealthIndicator(orchestrator));
1311
+ // How long the loop has stood still, and when — the number the console
1312
+ // reads for a node that went quiet.
1313
+ if (this.eventLoopWatch)
1314
+ titanHealth.registerIndicator(new EventLoopStallIndicator(this.eventLoopWatch));
1315
+ // The console asks every node for its own indicators, and a daemon has
1316
+ // no mesh connection to itself — so the local node's answer comes from
1317
+ // here. Registered beside the indicators rather than in the wiring
1318
+ // above, because this is the object that has just been given them.
1319
+ this.nodeManagerRpcService?.setTitanHealth(titanHealth);
687
1320
  }
688
1321
  catch {
689
1322
  // Health module may not be available — non-critical
@@ -692,18 +1325,17 @@ export class OmnitronDaemon {
692
1325
  // ============================================================================
693
1326
  // File Watching (dev mode)
694
1327
  // ============================================================================
695
- startFileWatcher(config, options, logger) {
696
- if (!options?.watch || options?.noWatch || !this.app)
1328
+ async startFileWatcher(config, options, logger) {
1329
+ if (!this.app)
697
1330
  return;
698
- this.app.container.resolveAsync(ORCHESTRATOR_TOKEN).then((orchestrator) => {
699
- orchestrator.devMode = true;
700
- this.fileWatcher = new FileWatcher(logger, orchestrator, config, process.cwd());
701
- this.fileWatcher.start(options.watchApps);
702
- const watched = this.fileWatcher.getWatchedApps();
703
- for (const w of watched) {
704
- logger.info({ app: w.name, directory: w.directory }, 'Watching for changes');
705
- }
706
- });
1331
+ const orchestrator = await this.app.container.resolveAsync(ORCHESTRATOR_TOKEN);
1332
+ const enabled = !!options?.watch && !options?.noWatch;
1333
+ orchestrator.devMode = enabled;
1334
+ await orchestrator.setWatchMode(enabled, options?.watchApps);
1335
+ if (!enabled)
1336
+ return;
1337
+ this.fileWatcher = new FileWatcher(logger, orchestrator, config, process.cwd());
1338
+ this.fileWatcher.start(options?.watchApps);
707
1339
  }
708
1340
  // ============================================================================
709
1341
  // Start Managed Apps
@@ -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');
903
1595
  return;
1596
+ }
1597
+ if (!this.nodeManagerService) {
1598
+ logger.warn({}, 'Health monitor not started — node manager was not constructed');
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
  // ============================================================================
@@ -1093,19 +1924,60 @@ export class OmnitronDaemon {
1093
1924
  }
1094
1925
  const config = await this.getConfig();
1095
1926
  this.fileWatcher = new FileWatcher(loggerModule.logger, orchestrator, config, process.cwd());
1927
+ orchestrator.devMode = true;
1928
+ await orchestrator.setWatchMode(true, apps);
1096
1929
  this.fileWatcher.start(apps);
1097
- return this.fileWatcher.getWatchedApps();
1930
+ return [...this.fileWatcher.getWatchedApps(), ...orchestrator.getBuildWatchedApps()];
1098
1931
  }
1099
- disableWatch() {
1100
- if (this.fileWatcher) {
1101
- this.fileWatcher.stop();
1102
- this.fileWatcher = null;
1932
+ /**
1933
+ * Point the file watcher at a reloaded config, if one is running.
1934
+ *
1935
+ * Public because `DaemonRpcService.reloadConfig` is the other half of the
1936
+ * same reload — the RPC and the SIGHUP handler must not diverge on what a
1937
+ * reload updates, which is exactly how the watcher came to be left out of
1938
+ * both.
1939
+ */
1940
+ async applyWatchConfig(config) {
1941
+ this.fileWatcher?.applyConfig(config);
1942
+ if (!this.app)
1943
+ return;
1944
+ const orchestrator = await this.app.container.resolveAsync(ORCHESTRATOR_TOKEN);
1945
+ for (const entry of config.apps) {
1946
+ const handle = orchestrator.getHandle(entry.name);
1947
+ // A bare name may resolve to a different registered project's app.
1948
+ // Only the same entry point may update that handle's watch policy.
1949
+ const source = entry.bootstrap ?? entry.script;
1950
+ const running = handle?.entry.bootstrap ?? handle?.entry.script;
1951
+ if (!handle || !source || !running ||
1952
+ path.resolve(entry.cwd ?? process.cwd(), source) !== path.resolve(handle.entry.cwd ?? process.cwd(), running))
1953
+ continue;
1954
+ const next = { ...handle.entry };
1955
+ if (entry.watch === undefined)
1956
+ delete next.watch;
1957
+ else
1958
+ next.watch = entry.watch;
1959
+ await orchestrator.redefineApp(handle.name, next);
1960
+ }
1961
+ }
1962
+ async disableWatch() {
1963
+ this.fileWatcher?.stop();
1964
+ this.fileWatcher = null;
1965
+ if (this.app) {
1966
+ const orchestrator = await this.app.container.resolveAsync(ORCHESTRATOR_TOKEN);
1967
+ await orchestrator.setWatchMode(false);
1103
1968
  }
1104
1969
  }
1105
- getWatchStatus() {
1106
- if (!this.fileWatcher)
1107
- return { enabled: false, apps: [] };
1108
- return { enabled: true, apps: this.fileWatcher.getWatchedApps() };
1970
+ /** Reports both watcher engines, including registered bootstrap projects. */
1971
+ async getWatchStatus() {
1972
+ const orchestrator = this.app
1973
+ ? await this.app.container.resolveAsync(ORCHESTRATOR_TOKEN) : null;
1974
+ const apps = [...(this.fileWatcher?.getWatchedApps() ?? []), ...(orchestrator?.getBuildWatchedApps() ?? [])];
1975
+ return {
1976
+ enabled: !!this.fileWatcher,
1977
+ watching: apps.length > 0,
1978
+ ...(apps.length ? {} : { reason: this.fileWatcher ? 'no enabled app matches the watch policy' : 'watching is disabled' }),
1979
+ apps,
1980
+ };
1109
1981
  }
1110
1982
  async getConfig() {
1111
1983
  const { loadEcosystemConfig } = await import('../config/loader.js');
@@ -1116,7 +1988,7 @@ export class OmnitronDaemon {
1116
1988
  // ============================================================================
1117
1989
  /**
1118
1990
  * Trigger graceful shutdown via Application's lifecycle system.
1119
- * Called by DaemonRpcService.shutdown() for the `omnitron shutdown` CLI command.
1991
+ * Called by DaemonRpcService.shutdown() for the `omnitron down` CLI command.
1120
1992
  */
1121
1993
  async stop() {
1122
1994
  if (!this.app)
@@ -1125,6 +1997,65 @@ export class OmnitronDaemon {
1125
1997
  // Application doesn't process.exit() for Manual reason — do it explicitly
1126
1998
  process.exit(0);
1127
1999
  }
2000
+ /**
2001
+ * SIGHUP handler — reload the ecosystem config without taking the
2002
+ * daemon down. Follows the Unix convention (`man 7 signal`: "many
2003
+ * daemons interpret SIGHUP as a request to reread their configs").
2004
+ * Reloads via the same code path the `reloadConfig` RPC uses so
2005
+ * orchestrator + RPC service both pick up the new entries.
2006
+ */
2007
+ async reloadConfigOnHup() {
2008
+ if (!this.app)
2009
+ return;
2010
+ // Loading first, on its own: a malformed file must be rejected before
2011
+ // anything is touched, and that failure genuinely means "the daemon is as
2012
+ // it was".
2013
+ const { loadEcosystemConfig } = await import('../config/loader.js');
2014
+ const newConfig = await loadEcosystemConfig();
2015
+ // Applying is the other half, and it has no such guarantee — each
2016
+ // destination takes the config in turn, so a failure in the middle leaves
2017
+ // the daemon split. `applyReloadedConfig` names what applied and what did
2018
+ // not, because "reload failed" reads as "nothing changed" and that is the
2019
+ // wrong reading exactly when it matters.
2020
+ //
2021
+ // The handler used to end in `catch (err) { throw err; }`, which did
2022
+ // nothing whatever. Removing it is trivial; what it was standing in front
2023
+ // of is not.
2024
+ const { applyReloadedConfig } = await import('./config-reload.js');
2025
+ await applyReloadedConfig(newConfig, [
2026
+ {
2027
+ name: 'orchestrator',
2028
+ apply: async (cfg) => {
2029
+ const orchestrator = await this.app.container.resolveAsync(ORCHESTRATOR_TOKEN);
2030
+ orchestrator.setConfig(cfg);
2031
+ },
2032
+ },
2033
+ {
2034
+ name: 'file watcher',
2035
+ // The watcher holds its own copy of the app list. Left out, a reload
2036
+ // updated who gets restarted but not who is watched: an app added to
2037
+ // the config was never watched, and one removed from it kept its
2038
+ // watchers and went on triggering restarts.
2039
+ apply: (cfg) => this.applyWatchConfig(cfg),
2040
+ },
2041
+ {
2042
+ name: 'RPC snapshot',
2043
+ // Exposed under DAEMON_SERVICE_ID; the reloadConfig RPC path mutates
2044
+ // `this.config` directly, so we mirror the same write. Unresolvable is
2045
+ // not a failure here — the service may not be exposed yet.
2046
+ apply: async (cfg) => {
2047
+ const { DAEMON_SERVICE_ID } = await import('../config/defaults.js');
2048
+ const daemonRpc = await this.app.container
2049
+ .resolveAsync(DAEMON_SERVICE_ID)
2050
+ .catch(() => null);
2051
+ if (daemonRpc) {
2052
+ daemonRpc.config = cfg;
2053
+ }
2054
+ },
2055
+ },
2056
+ ]);
2057
+ console.info('[omnitron] SIGHUP — config reloaded');
2058
+ }
1128
2059
  /**
1129
2060
  * Register all daemon shutdown tasks with Application's lifecycle system.
1130
2061
  *
@@ -1199,6 +2130,20 @@ export class OmnitronDaemon {
1199
2130
  }
1200
2131
  catch { /* non-critical */ }
1201
2132
  }, ShutdownPriority.First);
2133
+ // A release build outlives its daemon otherwise.
2134
+ //
2135
+ // Every step runs in its own process group, detached, so `pnpm install`
2136
+ // and the gate suite carry on after the daemon that started them is
2137
+ // gone — holding ports, writing into a release directory nothing will
2138
+ // ever finish, and for a quarter of an hour. Stopped first, with the
2139
+ // file watcher, because ending them is instant and everything after
2140
+ // this is slower.
2141
+ app.registerShutdownTask('stop-release-builds', () => {
2142
+ // It says what it stopped through its own logger; a shutdown task that
2143
+ // resolved the logger module to say one line would be one more thing
2144
+ // that can fail while the daemon is going down.
2145
+ this.releaseService?.stopAll();
2146
+ }, ShutdownPriority.First);
1202
2147
  // --- Priority: VeryHigh (10) — Stop managed apps (fast — 5s per app max) ---
1203
2148
  app.registerShutdownTask('stop-managed-apps', async () => {
1204
2149
  const orchestrator = await container.resolveAsync(ORCHESTRATOR_TOKEN);
@@ -1216,11 +2161,6 @@ export class OmnitronDaemon {
1216
2161
  await mc.stop();
1217
2162
  }
1218
2163
  catch { /* non-critical */ }
1219
- try {
1220
- const alert = await container.resolveAsync(ALERT_SERVICE_TOKEN);
1221
- alert.stop();
1222
- }
1223
- catch { /* non-critical */ }
1224
2164
  try {
1225
2165
  const fleet = await container.resolveAsync(FLEET_SERVICE_TOKEN);
1226
2166
  fleet.stopHeartbeatSweep();
@@ -1234,6 +2174,22 @@ export class OmnitronDaemon {
1234
2174
  }, ShutdownPriority.High);
1235
2175
  // --- Priority: Normal (50) — System workers, node manager, sync, cluster ---
1236
2176
  app.registerShutdownTask('stop-system-services', async () => {
2177
+ // The mesh first: stop following the registry, then drop the
2178
+ // connections. Each one may hold an SSH tunnel, and a tunnel that
2179
+ // outlives its daemon holds a socket and a remote session open with
2180
+ // nothing left to notice.
2181
+ try {
2182
+ this.meshHandle?.stop();
2183
+ }
2184
+ catch { /* non-critical */ }
2185
+ this.meshHandle = null;
2186
+ if (this.slaveConnector) {
2187
+ try {
2188
+ await this.slaveConnector.dispose();
2189
+ }
2190
+ catch { /* non-critical */ }
2191
+ this.slaveConnector = null;
2192
+ }
1237
2193
  if (this.syncService) {
1238
2194
  try {
1239
2195
  await this.syncService.stop();
@@ -1251,6 +2207,15 @@ export class OmnitronDaemon {
1251
2207
  this.configSyncService.stopPeriodicSync();
1252
2208
  }
1253
2209
  catch { /* non-critical */ }
2210
+ // Before stopping the workers: a worker exiting on the way out must not
2211
+ // be read as a crash and rescheduled.
2212
+ this.isShuttingDown = true;
2213
+ this.healthWorkerSpawn = null;
2214
+ if (this.healthWorkerRestartTimer) {
2215
+ clearTimeout(this.healthWorkerRestartTimer);
2216
+ this.healthWorkerRestartTimer = null;
2217
+ }
2218
+ this.stopFallbackNodeChecks();
1254
2219
  if (this.systemWorkerManager) {
1255
2220
  try {
1256
2221
  await this.systemWorkerManager.stopAll();
@@ -1297,10 +2262,30 @@ export class OmnitronDaemon {
1297
2262
  }
1298
2263
  catch { /* non-critical */ }
1299
2264
  if (this.infraService) {
1300
- try {
1301
- await this.infraService.teardown();
2265
+ // A node's infrastructure outlives its daemon.
2266
+ //
2267
+ // `teardown()` is `docker rm`, not `docker stop`, and this task runs
2268
+ // on every shutdown — so on a node, restarting the daemon would
2269
+ // remove the stack's Postgres, Redis and MinIO. `omnitron fleet
2270
+ // upgrade` restarts every node's daemon, which would have made a
2271
+ // routine upgrade take the test environment down with it.
2272
+ //
2273
+ // The daemon is the supervisor, not the owner of the data plane's
2274
+ // lifetime. Locally it still tears down — `omnitron down` there means
2275
+ // "I am done for the day" — and on either, `omnitron infra down` is
2276
+ // the command that removes containers on purpose.
2277
+ if (this.dc.role === 'slave') {
2278
+ // Said on the way out, because the absence of a teardown is
2279
+ // otherwise indistinguishable from a teardown that failed.
2280
+ this.infraService.dispose();
2281
+ console.info('[omnitron] leaving this node\'s infrastructure running — the daemon does not own its lifetime');
2282
+ }
2283
+ else {
2284
+ try {
2285
+ await this.infraService.teardown();
2286
+ }
2287
+ catch { /* best-effort */ }
1302
2288
  }
1303
- catch { /* best-effort */ }
1304
2289
  this.infraService = null;
1305
2290
  }
1306
2291
  }, ShutdownPriority.VeryLow);
@@ -1310,4 +2295,22 @@ export class OmnitronDaemon {
1310
2295
  }, ShutdownPriority.Last);
1311
2296
  }
1312
2297
  }
2298
+ /**
2299
+ * Call a method on a system worker's proxy.
2300
+ *
2301
+ * `ServiceProxy` is typed with an index signature whose values may be an
2302
+ * async iterable as well as a function, so a direct `worker.updateConfig(…)`
2303
+ * does not type-check. Resolving through one place keeps the cast in one
2304
+ * place, and returns without throwing when the worker is not running — the
2305
+ * only correct answer for "tell the health monitor" when there is none.
2306
+ */
2307
+ async function callWorkerMethod(manager, method, ...args) {
2308
+ const worker = manager.get('health-monitor');
2309
+ if (!worker)
2310
+ return undefined;
2311
+ const fn = worker[method];
2312
+ if (typeof fn !== 'function')
2313
+ return undefined;
2314
+ return fn.apply(worker, args);
2315
+ }
1313
2316
  //# sourceMappingURL=daemon.js.map