@oneuptime/common 10.4.11 → 10.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/Models/DatabaseModels/Alert.ts +332 -0
  2. package/Models/DatabaseModels/Incident.ts +332 -0
  3. package/Models/DatabaseModels/RunbookAgent.ts +16 -2
  4. package/Server/EnvironmentConfig.ts +16 -0
  5. package/Server/Infrastructure/ClickhouseConfig.ts +14 -0
  6. package/Server/Infrastructure/ClickhouseDatabase.ts +20 -3
  7. package/Server/Infrastructure/InMemoryTTLCache.ts +61 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.ts +253 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.ts +60 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  11. package/Server/Middleware/TelemetryIngest.ts +6 -38
  12. package/Server/Middleware/UserAuthorization.ts +1 -11
  13. package/Server/Services/AnalyticsDatabaseService.ts +33 -1
  14. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +6 -0
  15. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +1 -0
  16. package/Server/Services/OnCallDutyPolicyScheduleService.ts +17 -0
  17. package/Server/Services/TelemetryIngestionKeyService.ts +90 -1
  18. package/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.ts +108 -0
  19. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +13 -0
  20. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +25 -0
  21. package/Tests/Server/Middleware/UserAuthorization.test.ts +0 -7
  22. package/Types/Dashboard/DashboardComponentType.ts +0 -1
  23. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +2 -0
  24. package/Types/Dashboard/DashboardComponents/DashboardTableComponent.ts +74 -1
  25. package/Types/Dashboard/DashboardTemplates.ts +164 -51
  26. package/Types/Monitor/CriteriaFilter.ts +13 -0
  27. package/Types/Monitor/DnssecMonitor/DnssecMonitorResponse.ts +69 -0
  28. package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
  29. package/Types/Monitor/MonitorStep.ts +39 -0
  30. package/Types/Monitor/MonitorStepDnssecMonitor.ts +59 -0
  31. package/Types/Monitor/MonitorType.ts +18 -2
  32. package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +36 -9
  33. package/Types/Probe/ProbeMonitorResponse.ts +2 -0
  34. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +51 -0
  35. package/Utils/Dashboard/Components/DashboardTableComponent.ts +80 -17
  36. package/Utils/Dashboard/Components/Index.ts +0 -7
  37. package/Utils/Monitor/MonitorMetricType.ts +1 -0
  38. package/build/dist/Models/DatabaseModels/Alert.js +324 -0
  39. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  40. package/build/dist/Models/DatabaseModels/Incident.js +324 -0
  41. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  42. package/build/dist/Models/DatabaseModels/RunbookAgent.js +16 -2
  43. package/build/dist/Models/DatabaseModels/RunbookAgent.js.map +1 -1
  44. package/build/dist/Server/EnvironmentConfig.js +8 -0
  45. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  46. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +9 -1
  47. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  48. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +12 -3
  49. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
  50. package/build/dist/Server/Infrastructure/InMemoryTTLCache.js +49 -0
  51. package/build/dist/Server/Infrastructure/InMemoryTTLCache.js.map +1 -0
  52. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.js +100 -0
  53. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.js.map +1 -0
  54. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.js +28 -0
  55. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.js.map +1 -0
  56. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  57. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  58. package/build/dist/Server/Middleware/TelemetryIngest.js +2 -26
  59. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  60. package/build/dist/Server/Middleware/UserAuthorization.js +1 -7
  61. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  62. package/build/dist/Server/Services/AnalyticsDatabaseService.js +23 -2
  63. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  64. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +28 -24
  65. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  66. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +1 -1
  67. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  68. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +18 -2
  69. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  70. package/build/dist/Server/Services/TelemetryIngestionKeyService.js +83 -0
  71. package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
  72. package/build/dist/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.js +94 -0
  73. package/build/dist/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.js.map +1 -0
  74. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +10 -0
  75. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  76. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +22 -3
  77. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  78. package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js +0 -7
  79. package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js.map +1 -1
  80. package/build/dist/Types/Dashboard/DashboardComponentType.js +0 -1
  81. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  82. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +2 -0
  83. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  84. package/build/dist/Types/Dashboard/DashboardComponents/DashboardTableComponent.js +13 -1
  85. package/build/dist/Types/Dashboard/DashboardComponents/DashboardTableComponent.js.map +1 -1
  86. package/build/dist/Types/Dashboard/DashboardTemplates.js +142 -42
  87. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  88. package/build/dist/Types/Monitor/CriteriaFilter.js +12 -0
  89. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  90. package/build/dist/Types/Monitor/DnssecMonitor/DnssecMonitorResponse.js +2 -0
  91. package/build/dist/Types/Monitor/DnssecMonitor/DnssecMonitorResponse.js.map +1 -0
  92. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
  93. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  94. package/build/dist/Types/Monitor/MonitorStep.js +26 -0
  95. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  96. package/build/dist/Types/Monitor/MonitorStepDnssecMonitor.js +42 -0
  97. package/build/dist/Types/Monitor/MonitorStepDnssecMonitor.js.map +1 -0
  98. package/build/dist/Types/Monitor/MonitorType.js +16 -2
  99. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  100. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +27 -7
  101. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
  102. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +47 -0
  103. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  104. package/build/dist/Utils/Dashboard/Components/DashboardTableComponent.js +68 -16
  105. package/build/dist/Utils/Dashboard/Components/DashboardTableComponent.js.map +1 -1
  106. package/build/dist/Utils/Dashboard/Components/Index.js +0 -4
  107. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  108. package/build/dist/Utils/Monitor/MonitorMetricType.js +1 -0
  109. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  110. package/package.json +1 -2
  111. package/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.ts +0 -27
  112. package/Typings/elkjs.d.ts +0 -30
  113. package/Utils/Dashboard/Components/DashboardHostMetricChartComponent.ts +0 -132
  114. package/build/dist/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.js +0 -11
  115. package/build/dist/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.js.map +0 -1
  116. package/build/dist/Utils/Dashboard/Components/DashboardHostMetricChartComponent.js +0 -113
  117. package/build/dist/Utils/Dashboard/Components/DashboardHostMetricChartComponent.js.map +0 -1
@@ -5,13 +5,13 @@ import DashboardComponentType from "./DashboardComponentType";
5
5
  import DashboardChartType from "./Chart/ChartType";
6
6
  import ObjectID from "../ObjectID";
7
7
  import DashboardBaseComponent from "./DashboardComponents/DashboardBaseComponent";
8
+ import DashboardVariable, { DashboardVariableType } from "./DashboardVariable";
8
9
  import IconProp from "../Icon/IconProp";
9
10
  import MetricsAggregationType from "../Metrics/MetricsAggregationType";
10
11
  import IncidentMetricType from "../Incident/IncidentMetricType";
11
12
  import MonitorMetricType from "../Monitor/MonitorMetricType";
12
13
  import MetricDashboardMetricType from "../Metrics/MetricDashboardMetricType";
13
14
  import { DashboardValueTrendDirection } from "./DashboardComponents/DashboardValueComponent";
14
- import { HostMetricKind } from "./DashboardComponents/DashboardHostMetricChartComponent";
15
15
 
16
16
  /*
17
17
  * Trace / Exception / Profiles entries are intentionally not in this
@@ -88,6 +88,19 @@ interface MetricConfig {
88
88
  aggregationType: MetricsAggregationType;
89
89
  legend?: string;
90
90
  legendUnit?: string;
91
+ /*
92
+ * OpenTelemetry attribute keys to fan the query out across (e.g.
93
+ * ["host.name"] for one series per host). When set, the chart renders
94
+ * one series per unique value combination.
95
+ */
96
+ groupByAttributeKeys?: Array<string>;
97
+ /*
98
+ * Plot the per-second rate of change instead of the raw cumulative
99
+ * counter. Required for OTel cumulative counters such as
100
+ * `system.disk.io` and `system.network.io` so the chart shows I/O rate
101
+ * rather than bytes-since-boot.
102
+ */
103
+ transformAsRate?: boolean;
91
104
  }
92
105
 
93
106
  function buildMetricQueryConfig(config: MetricConfig): Record<string, unknown> {
@@ -104,8 +117,10 @@ function buildMetricQueryConfig(config: MetricConfig): Record<string, unknown> {
104
117
  metricName: config.metricName,
105
118
  aggegationType: config.aggregationType,
106
119
  },
107
- groupBy: undefined,
120
+ groupBy: config.groupByAttributeKeys ? { attributes: true } : undefined,
121
+ groupByAttributeKeys: config.groupByAttributeKeys,
108
122
  },
123
+ transformAsRate: config.transformAsRate,
109
124
  };
110
125
  }
111
126
 
@@ -383,6 +398,31 @@ function createKubernetesNodeListComponent(data: {
383
398
  };
384
399
  }
385
400
 
401
+ /*
402
+ * Template variables are pre-built TelemetryAttribute variables that
403
+ * ship with the template — the toolbar renders a "Cluster" /
404
+ * "Namespace" / "Host" picker without the user having to open the
405
+ * Variables modal. Resource attributes from the OTel collector are
406
+ * stored in ClickHouse under the `resource.` prefix (see the comment
407
+ * in MonitorAlert.ts), so the binding keys here mirror what the
408
+ * Kubernetes / Host detail pages already filter on.
409
+ */
410
+ function createTelemetryAttributeVariable(data: {
411
+ name: string;
412
+ label: string;
413
+ attributeKey: string;
414
+ isMultiSelect?: boolean;
415
+ }): DashboardVariable {
416
+ return {
417
+ id: ObjectID.generate().toString(),
418
+ name: data.name,
419
+ label: data.label,
420
+ type: DashboardVariableType.TelemetryAttribute,
421
+ attributeKey: data.attributeKey,
422
+ isMultiSelect: data.isMultiSelect ?? false,
423
+ };
424
+ }
425
+
386
426
  function createHostListComponent(data: {
387
427
  title: string;
388
428
  top: number;
@@ -412,33 +452,6 @@ function createHostListComponent(data: {
412
452
  };
413
453
  }
414
454
 
415
- function createHostMetricChartComponent(data: {
416
- title: string;
417
- description?: string;
418
- metricKind: HostMetricKind;
419
- top: number;
420
- left: number;
421
- width: number;
422
- height: number;
423
- }): DashboardBaseComponent {
424
- return {
425
- _type: ObjectType.DashboardComponent,
426
- componentType: DashboardComponentType.HostMetricChart,
427
- componentId: ObjectID.generate(),
428
- topInDashboardUnits: data.top,
429
- leftInDashboardUnits: data.left,
430
- widthInDashboardUnits: data.width,
431
- heightInDashboardUnits: data.height,
432
- minHeightInDashboardUnits: 3,
433
- minWidthInDashboardUnits: 6,
434
- arguments: {
435
- title: data.title,
436
- description: data.description,
437
- metricKind: data.metricKind,
438
- },
439
- };
440
- }
441
-
442
455
  // -- Dashboard configs --
443
456
 
444
457
  function createMonitorDashboardConfig(): DashboardViewConfig {
@@ -630,9 +643,25 @@ function createMonitorDashboardConfig(): DashboardViewConfig {
630
643
  }),
631
644
  ];
632
645
 
646
+ /*
647
+ * Monitor metrics are stored with bare attribute keys (`monitorName`,
648
+ * `probeName`) rather than the OTel `resource.*` prefix — see
649
+ * MonitorMetricUtil.buildAttributes — so the variable binds to the
650
+ * bare key. Multi-select lets users pin a small group of monitors.
651
+ */
652
+ const variables: Array<DashboardVariable> = [
653
+ createTelemetryAttributeVariable({
654
+ name: "monitor",
655
+ label: "Monitor",
656
+ attributeKey: "monitorName",
657
+ isMultiSelect: true,
658
+ }),
659
+ ];
660
+
633
661
  return {
634
662
  _type: ObjectType.DashboardViewConfig,
635
663
  components,
664
+ variables,
636
665
  heightInDashboardUnits: Math.max(DashboardSize.heightInDashboardUnits, 13),
637
666
  };
638
667
  }
@@ -1122,9 +1151,31 @@ function createKubernetesDashboardConfig(): DashboardViewConfig {
1122
1151
  }),
1123
1152
  ];
1124
1153
 
1154
+ /*
1155
+ * Pre-built variables let the user scope every widget on the dashboard
1156
+ * to a single cluster / namespace from the toolbar. Multi-select is on
1157
+ * for namespace so users can pick a couple of namespaces at once;
1158
+ * cluster stays single-select since the typical "compare two clusters"
1159
+ * workflow lives on the Compare page.
1160
+ */
1161
+ const variables: Array<DashboardVariable> = [
1162
+ createTelemetryAttributeVariable({
1163
+ name: "cluster",
1164
+ label: "Cluster",
1165
+ attributeKey: "resource.k8s.cluster.name",
1166
+ }),
1167
+ createTelemetryAttributeVariable({
1168
+ name: "namespace",
1169
+ label: "Namespace",
1170
+ attributeKey: "resource.k8s.namespace.name",
1171
+ isMultiSelect: true,
1172
+ }),
1173
+ ];
1174
+
1125
1175
  return {
1126
1176
  _type: ObjectType.DashboardViewConfig,
1127
1177
  components,
1178
+ variables,
1128
1179
  heightInDashboardUnits: Math.max(DashboardSize.heightInDashboardUnits, 21),
1129
1180
  };
1130
1181
  }
@@ -1442,9 +1493,25 @@ function createMetricsDashboardConfig(): DashboardViewConfig {
1442
1493
  }),
1443
1494
  ];
1444
1495
 
1496
+ /*
1497
+ * Most HTTP / runtime metrics on this dashboard carry the standard
1498
+ * OTel `resource.service.name` attribute, so scoping by service is
1499
+ * the most useful default. Multi-select keeps the cross-service view
1500
+ * available — e.g. compare API and worker on one chart.
1501
+ */
1502
+ const variables: Array<DashboardVariable> = [
1503
+ createTelemetryAttributeVariable({
1504
+ name: "service",
1505
+ label: "Service",
1506
+ attributeKey: "resource.service.name",
1507
+ isMultiSelect: true,
1508
+ }),
1509
+ ];
1510
+
1445
1511
  return {
1446
1512
  _type: ObjectType.DashboardViewConfig,
1447
1513
  components,
1514
+ variables,
1448
1515
  heightInDashboardUnits: Math.max(DashboardSize.heightInDashboardUnits, 24),
1449
1516
  };
1450
1517
  }
@@ -1453,11 +1520,10 @@ function createHostDashboardConfig(): DashboardViewConfig {
1453
1520
  /*
1454
1521
  * Layout notes:
1455
1522
  *
1456
- * - The HostMetricChart widget groups by `host.name` and plots one
1457
- * series per host (or a single series when filtered to one host).
1458
- * The OTel host receiver emits `system.cpu.utilization` and
1459
- * `system.memory.utilization` as [0, 1] ratios, which Value/Gauge
1460
- * widgets auto-scale to percent at render time (see
1523
+ * - Per-host charts fan out via `groupByAttributeKeys: ["host.name"]`
1524
+ * so a single chart renders one series per host. The OTel host
1525
+ * receiver emits `system.cpu.utilization` as a [0, 1] ratio, which
1526
+ * Value/Gauge widgets auto-scale to percent at render time (see
1461
1527
  * splitFormattedValue / isFractionScale).
1462
1528
  *
1463
1529
  * - The Value widget for "Avg Memory" uses `system.memory.usage`
@@ -1465,11 +1531,10 @@ function createHostDashboardConfig(): DashboardViewConfig {
1465
1531
  * `system.memory.utilization` is not always emitted by default OTel
1466
1532
  * host metrics.
1467
1533
  *
1468
- * - Disk and Network I/O Value widgets aggregate the raw byte counters
1469
- * with Sum. HostMetricChart turns these into per-second rates for
1470
- * the chart view; the Value tile shows the unrated total so users
1471
- * reading "Disk I/O" against the time window get an absolute byte
1472
- * figure rather than a noisy snapshot.
1534
+ * - Disk and Network I/O charts set `transformAsRate: true` so the
1535
+ * cumulative byte counters render as per-second rates rather than
1536
+ * bytes-since-boot. The matching Value tiles in row 1 keep the
1537
+ * unrated Sum so users see an absolute byte figure over the window.
1473
1538
  */
1474
1539
  const components: Array<DashboardBaseComponent> = [
1475
1540
  // Row 0: Title
@@ -1529,21 +1594,31 @@ function createHostDashboardConfig(): DashboardViewConfig {
1529
1594
  }),
1530
1595
 
1531
1596
  // Row 2-4: Per-host CPU and Memory charts
1532
- createHostMetricChartComponent({
1597
+ createChartComponent({
1533
1598
  title: "CPU Utilization by Host",
1534
- metricKind: HostMetricKind.CpuUtilization,
1599
+ chartType: DashboardChartType.Line,
1535
1600
  top: 2,
1536
1601
  left: 0,
1537
1602
  width: 6,
1538
1603
  height: 3,
1604
+ metricConfig: {
1605
+ metricName: MetricDashboardMetricType.SystemCpuUtilization,
1606
+ aggregationType: MetricsAggregationType.Avg,
1607
+ groupByAttributeKeys: ["host.name"],
1608
+ },
1539
1609
  }),
1540
- createHostMetricChartComponent({
1610
+ createChartComponent({
1541
1611
  title: "Memory Usage by Host",
1542
- metricKind: HostMetricKind.MemoryUsage,
1612
+ chartType: DashboardChartType.Line,
1543
1613
  top: 2,
1544
1614
  left: 6,
1545
1615
  width: 6,
1546
1616
  height: 3,
1617
+ metricConfig: {
1618
+ metricName: MetricDashboardMetricType.SystemMemoryUsage,
1619
+ aggregationType: MetricsAggregationType.Avg,
1620
+ groupByAttributeKeys: ["host.name"],
1621
+ },
1547
1622
  }),
1548
1623
 
1549
1624
  // Row 5: Section header
@@ -1600,13 +1675,18 @@ function createHostDashboardConfig(): DashboardViewConfig {
1600
1675
  aggregationType: MetricsAggregationType.Avg,
1601
1676
  },
1602
1677
  }),
1603
- createHostMetricChartComponent({
1678
+ createChartComponent({
1604
1679
  title: "Filesystem Usage by Host",
1605
- metricKind: HostMetricKind.Filesystem,
1680
+ chartType: DashboardChartType.Line,
1606
1681
  top: 11,
1607
1682
  left: 4,
1608
1683
  width: 8,
1609
1684
  height: 3,
1685
+ metricConfig: {
1686
+ metricName: "system.filesystem.usage",
1687
+ aggregationType: MetricsAggregationType.Avg,
1688
+ groupByAttributeKeys: ["host.name"],
1689
+ },
1610
1690
  }),
1611
1691
 
1612
1692
  // Row 14: Section header
@@ -1620,21 +1700,33 @@ function createHostDashboardConfig(): DashboardViewConfig {
1620
1700
  }),
1621
1701
 
1622
1702
  // Row 15-17: Disk and network I/O rates per host
1623
- createHostMetricChartComponent({
1703
+ createChartComponent({
1624
1704
  title: "Disk I/O by Host",
1625
- metricKind: HostMetricKind.DiskIo,
1705
+ chartType: DashboardChartType.Line,
1626
1706
  top: 15,
1627
1707
  left: 0,
1628
1708
  width: 6,
1629
1709
  height: 3,
1710
+ metricConfig: {
1711
+ metricName: MetricDashboardMetricType.SystemDiskIo,
1712
+ aggregationType: MetricsAggregationType.Sum,
1713
+ groupByAttributeKeys: ["host.name"],
1714
+ transformAsRate: true,
1715
+ },
1630
1716
  }),
1631
- createHostMetricChartComponent({
1717
+ createChartComponent({
1632
1718
  title: "Network I/O by Host",
1633
- metricKind: HostMetricKind.NetworkIo,
1719
+ chartType: DashboardChartType.Line,
1634
1720
  top: 15,
1635
1721
  left: 6,
1636
1722
  width: 6,
1637
1723
  height: 3,
1724
+ metricConfig: {
1725
+ metricName: MetricDashboardMetricType.SystemNetworkIo,
1726
+ aggregationType: MetricsAggregationType.Sum,
1727
+ groupByAttributeKeys: ["host.name"],
1728
+ transformAsRate: true,
1729
+ },
1638
1730
  }),
1639
1731
 
1640
1732
  // Row 18: Section header
@@ -1648,13 +1740,18 @@ function createHostDashboardConfig(): DashboardViewConfig {
1648
1740
  }),
1649
1741
 
1650
1742
  // Row 19-21: Process count chart and recent logs
1651
- createHostMetricChartComponent({
1743
+ createChartComponent({
1652
1744
  title: "Process Count by Host",
1653
- metricKind: HostMetricKind.ProcessCount,
1745
+ chartType: DashboardChartType.Line,
1654
1746
  top: 19,
1655
1747
  left: 0,
1656
1748
  width: 6,
1657
1749
  height: 3,
1750
+ metricConfig: {
1751
+ metricName: "system.processes.count",
1752
+ aggregationType: MetricsAggregationType.Avg,
1753
+ groupByAttributeKeys: ["host.name"],
1754
+ },
1658
1755
  }),
1659
1756
  createLogStreamComponent({
1660
1757
  title: "Recent Logs",
@@ -1665,9 +1762,25 @@ function createHostDashboardConfig(): DashboardViewConfig {
1665
1762
  }),
1666
1763
  ];
1667
1764
 
1765
+ /*
1766
+ * Per-host scoping: the variable defaults to multi-select because the
1767
+ * dashboard's by-host charts already split rendering per host; the
1768
+ * selector lets users narrow to a subset (e.g. just two prod hosts)
1769
+ * without losing the per-host breakdown.
1770
+ */
1771
+ const variables: Array<DashboardVariable> = [
1772
+ createTelemetryAttributeVariable({
1773
+ name: "host",
1774
+ label: "Host",
1775
+ attributeKey: "resource.host.name",
1776
+ isMultiSelect: true,
1777
+ }),
1778
+ ];
1779
+
1668
1780
  return {
1669
1781
  _type: ObjectType.DashboardViewConfig,
1670
1782
  components,
1783
+ variables,
1671
1784
  heightInDashboardUnits: Math.max(DashboardSize.heightInDashboardUnits, 22),
1672
1785
  };
1673
1786
  }
@@ -84,6 +84,14 @@ export enum CheckOn {
84
84
  DomainStatusCode = "Domain Status Code",
85
85
  DomainIsExpired = "Domain Is Expired",
86
86
 
87
+ // DNSSEC monitors.
88
+ DnssecChainValid = "DNSSEC Chain Is Valid",
89
+ DnssecDnskeyExists = "DNSSEC DNSKEY Record Exists",
90
+ DnssecDsExists = "DNSSEC DS Record Exists At Parent",
91
+ DnssecSignatureExpiresInDays = "DNSSEC Signature Expires In Days",
92
+ DnssecResolverConsensus = "DNSSEC Resolver Consensus (AD Flag)",
93
+ DnssecNameserverConsistent = "DNSSEC Nameservers Are Consistent",
94
+
87
95
  // External Status Page monitors.
88
96
  ExternalStatusPageIsOnline = "External Status Page Is Online",
89
97
  ExternalStatusPageOverallStatus = "External Status Page Overall Status",
@@ -279,6 +287,11 @@ export class CriteriaFilterUtil {
279
287
  checkOn === CheckOn.SnmpIsOnline ||
280
288
  checkOn === CheckOn.DnsIsOnline ||
281
289
  checkOn === CheckOn.DomainIsExpired ||
290
+ checkOn === CheckOn.DnssecChainValid ||
291
+ checkOn === CheckOn.DnssecDnskeyExists ||
292
+ checkOn === CheckOn.DnssecDsExists ||
293
+ checkOn === CheckOn.DnssecResolverConsensus ||
294
+ checkOn === CheckOn.DnssecNameserverConsistent ||
282
295
  checkOn === CheckOn.ExternalStatusPageIsOnline
283
296
  ) {
284
297
  return false;
@@ -0,0 +1,69 @@
1
+ export interface DnssecKeyRecord {
2
+ flags: number;
3
+ algorithm: number;
4
+ keyTag?: number | undefined;
5
+ }
6
+
7
+ export interface DnssecDsRecord {
8
+ keyTag: number;
9
+ algorithm: number;
10
+ digestType: number;
11
+ digest: string;
12
+ }
13
+
14
+ export interface DnssecRrsigRecord {
15
+ typeCovered: string;
16
+ algorithm: number;
17
+ signerName: string;
18
+ keyTag: number;
19
+ inception?: string | undefined;
20
+ expiration?: string | undefined;
21
+ }
22
+
23
+ export interface DnssecResolverCheck {
24
+ resolver: string;
25
+ adFlag: boolean;
26
+ servfailWhenValidating: boolean;
27
+ error?: string | undefined;
28
+ }
29
+
30
+ export interface DnssecNameserverCheck {
31
+ nameServer: string;
32
+ soaSerial?: string | undefined;
33
+ rrsigExpiration?: string | undefined;
34
+ error?: string | undefined;
35
+ }
36
+
37
+ export default interface DnssecMonitorResponse {
38
+ isOnline: boolean;
39
+ responseTimeInMs: number;
40
+ failureCause: string;
41
+ domainName: string;
42
+ isTimeout?: boolean | undefined;
43
+
44
+ // Zone signed?
45
+ isZoneSigned: boolean;
46
+
47
+ // DNSKEY presence
48
+ dnskeys: Array<DnssecKeyRecord>;
49
+
50
+ // DS at parent
51
+ parentDsRecords: Array<DnssecDsRecord>;
52
+ isParentDsPresent: boolean;
53
+
54
+ // RRSIG over the A record (zone apex by default)
55
+ rrsigs: Array<DnssecRrsigRecord>;
56
+ earliestSignatureExpiration?: string | undefined;
57
+ daysUntilSignatureExpiry?: number | undefined;
58
+
59
+ // Resolver consensus (AD flag + CD-bit SERVFAIL test)
60
+ resolverChecks: Array<DnssecResolverCheck>;
61
+ resolverConsensusAd: boolean;
62
+
63
+ // Primary/secondary nameserver consistency
64
+ nameserverChecks: Array<DnssecNameserverCheck>;
65
+ isNameserverConsistent: boolean;
66
+
67
+ // Overall chain validity (DNSKEY exists, DS exists, RRSIG valid, AD across resolvers)
68
+ isChainValid: boolean;
69
+ }
@@ -487,6 +487,33 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
487
487
  return monitorCriteriaInstance;
488
488
  }
489
489
 
490
+ if (arg.monitorType === MonitorType.DNSSEC) {
491
+ const monitorCriteriaInstance: MonitorCriteriaInstance =
492
+ new MonitorCriteriaInstance();
493
+
494
+ monitorCriteriaInstance.data = {
495
+ id: ObjectID.generate().toString(),
496
+ monitorStatusId: arg.monitorStatusId,
497
+ filterCondition: FilterCondition.All,
498
+ filters: [
499
+ {
500
+ checkOn: CheckOn.DnssecChainValid,
501
+ filterType: FilterType.True,
502
+ value: undefined,
503
+ },
504
+ ],
505
+ incidents: [],
506
+ alerts: [],
507
+ createAlerts: false,
508
+ changeMonitorStatus: true,
509
+ createIncidents: false,
510
+ name: `Check if ${arg.monitorName} DNSSEC chain is valid`,
511
+ description: `This criteria checks if the ${arg.monitorName} DNSSEC chain is valid end-to-end`,
512
+ };
513
+
514
+ return monitorCriteriaInstance;
515
+ }
516
+
490
517
  if (arg.monitorType === MonitorType.ExternalStatusPage) {
491
518
  const monitorCriteriaInstance: MonitorCriteriaInstance =
492
519
  new MonitorCriteriaInstance();
@@ -695,6 +722,46 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
695
722
  };
696
723
  }
697
724
 
725
+ if (arg.monitorType === MonitorType.DNSSEC) {
726
+ monitorCriteriaInstance.data = {
727
+ id: ObjectID.generate().toString(),
728
+ monitorStatusId: arg.monitorStatusId,
729
+ filterCondition: FilterCondition.Any,
730
+ filters: [
731
+ {
732
+ checkOn: CheckOn.DnssecChainValid,
733
+ filterType: FilterType.False,
734
+ value: undefined,
735
+ },
736
+ ],
737
+ incidents: [
738
+ {
739
+ title: `${arg.monitorName} DNSSEC chain is broken`,
740
+ description: `${arg.monitorName} DNSSEC validation is currently failing.`,
741
+ incidentSeverityId: arg.incidentSeverityId,
742
+ autoResolveIncident: true,
743
+ id: ObjectID.generate().toString(),
744
+ onCallPolicyIds: [],
745
+ },
746
+ ],
747
+ changeMonitorStatus: true,
748
+ createIncidents: true,
749
+ createAlerts: false,
750
+ alerts: [
751
+ {
752
+ title: `${arg.monitorName} DNSSEC chain is broken`,
753
+ description: `${arg.monitorName} DNSSEC validation is currently failing.`,
754
+ alertSeverityId: arg.alertSeverityId,
755
+ autoResolveAlert: true,
756
+ id: ObjectID.generate().toString(),
757
+ onCallPolicyIds: [],
758
+ },
759
+ ],
760
+ name: `Check if ${arg.monitorName} DNSSEC chain is broken`,
761
+ description: `This criteria checks if the ${arg.monitorName} DNSSEC chain is broken`,
762
+ };
763
+ }
764
+
698
765
  if (arg.monitorType === MonitorType.ExternalStatusPage) {
699
766
  monitorCriteriaInstance.data = {
700
767
  id: ObjectID.generate().toString(),
@@ -38,6 +38,9 @@ import MonitorStepDnsMonitor, {
38
38
  import MonitorStepDomainMonitor, {
39
39
  MonitorStepDomainMonitorUtil,
40
40
  } from "./MonitorStepDomainMonitor";
41
+ import MonitorStepDnssecMonitor, {
42
+ MonitorStepDnssecMonitorUtil,
43
+ } from "./MonitorStepDnssecMonitor";
41
44
  import MonitorStepExternalStatusPageMonitor, {
42
45
  MonitorStepExternalStatusPageMonitorUtil,
43
46
  } from "./MonitorStepExternalStatusPageMonitor";
@@ -109,6 +112,9 @@ export interface MonitorStepType {
109
112
  // Domain monitor
110
113
  domainMonitor?: MonitorStepDomainMonitor | undefined;
111
114
 
115
+ // DNSSEC monitor
116
+ dnssecMonitor?: MonitorStepDnssecMonitor | undefined;
117
+
112
118
  // External Status Page monitor
113
119
  externalStatusPageMonitor?: MonitorStepExternalStatusPageMonitor | undefined;
114
120
 
@@ -150,6 +156,7 @@ export default class MonitorStep extends DatabaseProperty {
150
156
  snmpMonitor: undefined,
151
157
  dnsMonitor: undefined,
152
158
  domainMonitor: undefined,
159
+ dnssecMonitor: undefined,
153
160
  externalStatusPageMonitor: undefined,
154
161
  kubernetesMonitor: undefined,
155
162
  dockerMonitor: undefined,
@@ -191,6 +198,7 @@ export default class MonitorStep extends DatabaseProperty {
191
198
  snmpMonitor: undefined,
192
199
  dnsMonitor: undefined,
193
200
  domainMonitor: undefined,
201
+ dnssecMonitor: undefined,
194
202
  externalStatusPageMonitor: undefined,
195
203
  kubernetesMonitor: undefined,
196
204
  dockerMonitor: undefined,
@@ -334,6 +342,13 @@ export default class MonitorStep extends DatabaseProperty {
334
342
  return this;
335
343
  }
336
344
 
345
+ public setDnssecMonitor(
346
+ dnssecMonitor: MonitorStepDnssecMonitor,
347
+ ): MonitorStep {
348
+ this.data!.dnssecMonitor = dnssecMonitor;
349
+ return this;
350
+ }
351
+
337
352
  public setExternalStatusPageMonitor(
338
353
  externalStatusPageMonitor: MonitorStepExternalStatusPageMonitor,
339
354
  ): MonitorStep {
@@ -508,6 +523,23 @@ export default class MonitorStep extends DatabaseProperty {
508
523
  }
509
524
  }
510
525
 
526
+ if (monitorType === MonitorType.DNSSEC) {
527
+ if (!value.data.dnssecMonitor) {
528
+ return "DNSSEC configuration is required";
529
+ }
530
+
531
+ if (!value.data.dnssecMonitor.domainName) {
532
+ return "Domain name is required";
533
+ }
534
+
535
+ if (
536
+ !value.data.dnssecMonitor.resolvers ||
537
+ value.data.dnssecMonitor.resolvers.length === 0
538
+ ) {
539
+ return "At least one resolver is required";
540
+ }
541
+ }
542
+
511
543
  if (monitorType === MonitorType.ExternalStatusPage) {
512
544
  if (!value.data.externalStatusPageMonitor) {
513
545
  return "External status page configuration is required";
@@ -600,6 +632,9 @@ export default class MonitorStep extends DatabaseProperty {
600
632
  domainMonitor: this.data.domainMonitor
601
633
  ? MonitorStepDomainMonitorUtil.toJSON(this.data.domainMonitor)
602
634
  : undefined,
635
+ dnssecMonitor: this.data.dnssecMonitor
636
+ ? MonitorStepDnssecMonitorUtil.toJSON(this.data.dnssecMonitor)
637
+ : undefined,
603
638
  externalStatusPageMonitor: this.data.externalStatusPageMonitor
604
639
  ? MonitorStepExternalStatusPageMonitorUtil.toJSON(
605
640
  this.data.externalStatusPageMonitor,
@@ -734,6 +769,9 @@ export default class MonitorStep extends DatabaseProperty {
734
769
  domainMonitor: json["domainMonitor"]
735
770
  ? (json["domainMonitor"] as JSONObject)
736
771
  : undefined,
772
+ dnssecMonitor: json["dnssecMonitor"]
773
+ ? (json["dnssecMonitor"] as JSONObject)
774
+ : undefined,
737
775
  externalStatusPageMonitor: json["externalStatusPageMonitor"]
738
776
  ? (json["externalStatusPageMonitor"] as JSONObject)
739
777
  : undefined,
@@ -775,6 +813,7 @@ export default class MonitorStep extends DatabaseProperty {
775
813
  snmpMonitor: Zod.any().optional(),
776
814
  dnsMonitor: Zod.any().optional(),
777
815
  domainMonitor: Zod.any().optional(),
816
+ dnssecMonitor: Zod.any().optional(),
778
817
  externalStatusPageMonitor: Zod.any().optional(),
779
818
  kubernetesMonitor: Zod.any().optional(),
780
819
  dockerMonitor: Zod.any().optional(),