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