NIS2 KPI Telemetry Schema v1

Contract ID: CONTRACT-NIS2-KPI-TELEMETRY-V1-019 Status: Active (Stella Ops EU compliance sprint 060) Owner: Telemetry Guild Schema version: nis2-kpi-telemetry-schema-v1 Source defaults: docs/europe/nis2-kpi-targets-defaults.yaml Input contract: nis2-control-register-v1

Part of the Stella Ops Contracts index. Audience: Telemetry implementers who own the NIS2 metric family, fact-owning modules (Policy, Findings, Authority, Crypto) that emit samples, and Console/dashboard teams that consume the read projection.

Purpose

This contract defines the Prometheus/OpenTelemetry metric family used to measure NIS2 effectiveness across the thirteen thematic areas from the EU compliance closure plan. The catalog is pinned to docs/europe/nis2-kpi-targets-defaults.yaml; tenant target overrides are configuration owned by downstream compliance-profile work and must not change the metric names.

The read projection (nis2-effectiveness-dashboard-v1, see Consumption) is in turn the input to the NIS2 Effectiveness Report monthly export.

Schema Pinning

Metric Naming

Metric names use:

nis2_area_<area>_<metric>_<unit>

Rules:

Each gauge instrument also carries an OpenTelemetry unit string derived from the metric unit (Nis2AreaMetricDefinition.InstrumentUnit):

Schema unitOTel instrument unit
percent%
count{count}
daysd
minutesmin
hoursh

Labels

Every metric sample emits exactly these bounded labels:

LabelRequiredNotes
tenantyesTenant id as an opaque identifier. Must not contain customer names, emails, or human PII.
control_idyesStable NIS2 control id from the control register, for example NIS2-09-CRYPTOGRAPHY.
severityyesok, warning, critical, or unknown; derived from thresholds unless the producer supplies a reviewed override.

Evidence refs are not metric labels. Producers may keep evidence refs in reports or evidence bundles, but they must not emit them as Prometheus labels.

Emission API

Telemetry Core owns the metric family and bounded in-memory reporting surface. Fact-owning modules emit samples by calling:

await nis2AreaMetrics.RecordMeasurementAsync(new Nis2AreaMetricMeasurement(
    TenantId: tenantId,
    ControlId: controlId,
    MetricName: "nis2_area_9_deprecated_crypto_findings_count",
    Value: deprecatedFindingCount,
    ObservedAt: observedAt,
    EvidenceRef: "evidence://cryptography/trust-profile/current",
    Severity: null));

Nis2AreaMetricMeasurement fields (record positional parameters): TenantId, ControlId, MetricName, Value, ObservedAt, optional EvidenceRef (default null), and optional Severity (default null). On record, the service:

Recording a sample also emits a threshold-breach event through INis2EffectivenessThresholdBreachEmitter when the value crosses into warning/critical and the severity worsens relative to the previously recorded value for the same tenant/control/metric series. Hosts that only need the gauge surface and reports get a no-op emitter (NoOpNis2EffectivenessThresholdBreachEmitter); hosts wiring downstream notifications register a live emitter. When the caller supplies no EvidenceRef, the breach payload auto-generates a deterministic telemetry://nis2/effectiveness/<tenant>/<areaId>/<control>/<metricKey>/<observedAtUnixMs> reference.

Direct call-site adoption by Policy, Findings, Authority, and Crypto is downstream module work. Telemetry Core does not read another module’s persistence schema directly.

Threshold Semantics

Catalog

AreaEvidence modeMetric keyMetric nameUnitTargetWarningCritical
1mixedpolicyCoveragePercentnis2_area_1_policy_coverage_percentpercentmin 100below 100below 95
1mixedoverduePolicyReviewsnis2_area_1_overdue_policy_reviews_countcountmax 0above 0above 2
2mixedriskAssessmentCurrencyDaysnis2_area_2_risk_assessment_currency_daysdaysmax 365above 365above 450
2mixedhighRiskTreatmentCoveragePercentnis2_area_2_high_risk_treatment_coverage_percentpercentmin 95below 95below 90
3mixedearlyWarningSloPercentnis2_area_3_early_warning_slo_percentpercentmin 100below 100below 98
3mixedincidentNotificationSloPercentnis2_area_3_incident_notification_slo_percentpercentmin 100below 100below 98
3mixedmeanClassificationMinutesnis2_area_3_mean_classification_minutesminutesmax 60above 60above 240
4mixedrecoveryRunbookCurrencyDaysnis2_area_4_recovery_runbook_currency_daysdaysmax 365above 365above 450
4mixedrestoreTestSuccessPercentnis2_area_4_restore_test_success_percentpercentmin 95below 95below 90
5stella-nativecriticalSupplierAssessmentCoveragePercentnis2_area_5_critical_supplier_assessment_coverage_percentpercentmin 95below 95below 90
5stella-nativecriticalSupplierContractCoveragePercentnis2_area_5_critical_supplier_contract_coverage_percentpercentmin 100below 100below 95
5stella-nativesignedArtifactCoveragePercentnis2_area_5_signed_artifact_coverage_percentpercentmin 100below 100below 98
6stella-nativereleasePolicyGateCoveragePercentnis2_area_6_release_policy_gate_coverage_percentpercentmin 100below 100below 98
6stella-nativecriticalVulnerabilityTriageHoursnis2_area_6_critical_vulnerability_triage_hourshoursmax 24above 24above 48
6stella-nativehighVulnerabilityRemediationDaysnis2_area_6_high_vulnerability_remediation_daysdaysmax 30above 30above 90
7mixedcontrolTestCurrencyDaysnis2_area_7_control_test_currency_daysdaysmax 90above 90above 180
7mixedevidenceFreshnessDaysnis2_area_7_evidence_freshness_daysdaysmax 30above 30above 90
8tenant-suppliedtrainingAttestationCoveragePercentnis2_area_8_training_attestation_coverage_percentpercentmin 95below 95below 90
8tenant-suppliedhygieneReviewCurrencyDaysnis2_area_8_hygiene_review_currency_daysdaysmax 365above 365above 450
9stella-nativedeprecatedCryptoFindingsnis2_area_9_deprecated_crypto_findings_countcountmax 0above 0above 1
9stella-nativeoverdueKeyRotationsPercentnis2_area_9_overdue_key_rotations_percentpercentmax 0above 0above 5
10tenant-suppliedprivilegedAccessReviewCurrencyDaysnis2_area_10_privileged_access_review_currency_daysdaysmax 90above 90above 180
10tenant-suppliedjoinerMoverLeaverCompletionHoursnis2_area_10_joiner_mover_leaver_completion_hourshoursmax 24above 24above 72
11mixedprivilegedMfaCoveragePercentnis2_area_11_privileged_mfa_coverage_percentpercentmin 100below 100below 98
11mixedstalePrivilegedAccountsnis2_area_11_stale_privileged_accounts_countcountmax 0above 0above 1
11mixedbreakGlassTestCurrencyDaysnis2_area_11_break_glass_test_currency_daysdaysmax 180above 180above 365
12mixedinventoryFreshnessMinutesnis2_area_12_inventory_freshness_minutesminutesmax 30above 30above 120
12mixedcriticalAssetOwnerCoveragePercentnis2_area_12_critical_asset_owner_coverage_percentpercentmin 100below 100below 95
12mixedunknownCriticalAssetsnis2_area_12_unknown_critical_assets_countcountmax 0above 0above 1
13tenant-suppliedphysicalControlAttestationCurrencyDaysnis2_area_13_physical_control_attestation_currency_daysdaysmax 365above 365above 450
13tenant-supplieddatacenterProviderEvidenceCurrencyDaysnis2_area_13_datacenter_provider_evidence_currency_daysdaysmax 365above 365above 450

Replay And Offline Verification

Consumption

The recorded samples are surfaced to the Console/dashboard through a local, tenant-scoped read API on the Platform WebService (src/Platform/StellaOps.Platform.WebService/Endpoints/Nis2TelemetryEndpoints.cs). This is a read projection of the metric family above and emits no external calls.

Security And Privacy