semconv

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: AGPL-3.0 Imports: 1 Imported by: 0

Documentation

Overview

Package semconv centralizes the OpenTelemetry attribute keys and UCUM units shared across collectors and the telemetry package, so naming stays consistent as new collectors land (entra.*, intune.*) alongside the self-observability signals defined here.

Index

Constants

View Source
const (
	// AttrMetricName names the source metric a graph2otel.series.* gauge point
	// describes (e.g. "entra.signin.count").
	AttrMetricName = "metric.name"
	// AttrClipMode says what became of the series graph2otel.series.clipped
	// counts: "folded" (summed into the `other` bucket, because the metric is
	// additive) or "dropped" (discarded, because summing it would have emitted a
	// number that was never measured). Two values, fixed (#235).
	AttrClipMode = "mode"
)

Self-observability attribute keys, used by the telemetry package's cardinality tracker (internal/telemetry.CardinalityTracker) to label its graph2otel.series.* gauges.

View Source
const (
	// AttrCollector names the collector a scrape.* metric point describes
	// (e.g. "devices", "auditlogs").
	AttrCollector = "collector"
	// AttrField names the wire field a graph2otel.api.unexpected point describes.
	// Bounded: the value is a field name from graph2otel's own source, never data.
	AttrField = "field"
	// AttrKind names the class of a graph2otel.api.unexpected finding — see
	// internal/wirecheck for the bounded value set.
	AttrKind = "kind"
	// AttrTenantID identifies which tenant produced a record. It is on EVERY
	// signal — self-obs and domain, metrics and logs (#143).
	//
	// Two writers set it, deliberately:
	//   - collector/selfobs.go stamps it on scrape.*/checkpoint.* via selfObsAttrs.
	//   - telemetry.WithTenant stamps everything else at the emitter boundary,
	//     which is the seam that reaches all 58 collectors. First stamp wins, so
	//     the self-obs value above passes through untouched.
	//
	// Bounded cardinality: one value per operator-configured tenant. It grows with
	// tenant COUNT, never with tenant SIZE, which is what the #112 rule forbids —
	// so it is metric-label-safe, and internal/signalcapture correctly does not
	// flag it.
	//
	// It is a METRIC label, unlike AttrIngestTransport below. That asymmetry is
	// deliberate and is the whole point: there is one MeterProvider and one OTLP
	// resource per process, so without this label two tenants' domain metrics are
	// not merely unsliceable — they are the same series, interleaving samples into
	// a meaningless number.
	//
	// Empty means "no tenant configured" and stamps nothing, keeping single-tenant
	// deploys byte-identical.
	AttrTenantID = "tenant_id"
)

Collector self-observability attribute keys, used by internal/collector's Scheduler to label its graph2otel.scrape.* and graph2otel.checkpoint.* metrics.

View Source
const (
	UnitSeries        = "{series}"
	UnitDimensionless = "1"
	// UnitSeconds is used by the collector self-obs duration/staleness/budget gauges.
	UnitSeconds = "s"
)

UCUM units used by the telemetry package's self-observability metrics.

View Source
const (
	AttrAadDeviceId                                  = "aad_device_id"
	AttrAccountDomain                                = "account_domain"
	AttrAccountName                                  = "account_name"
	AttrAccountObjectId                              = "account_object_id"
	AttrAccountSid                                   = "account_sid"
	AttrAccountUpn                                   = "account_upn"
	AttrAction                                       = "action"
	AttrActionResult                                 = "action_result"
	AttrActionTrigger                                = "action_trigger"
	AttrActionType                                   = "action_type"
	AttrAdditionalFields                             = "additional_fields"
	AttrAlertId                                      = "alert_id"
	AttrAppGuardContainerId                          = "app_guard_container_id"
	AttrAppVersion                                   = "app_version"
	AttrApplication                                  = "application"
	AttrApplicationId                                = "application_id"
	AttrAssetValue                                   = "asset_value"
	AttrAttachmentCount                              = "attachment_count"
	AttrAttackTechniques                             = "attack_techniques"
	AttrAuthenticationDetails                        = "authentication_details"
	AttrAwsResourceName                              = "aws_resource_name"
	AttrAzureResourceId                              = "azure_resource_id"
	AttrAzureVmId                                    = "azure_vm_id"
	AttrAzureVmSubscriptionId                        = "azure_vm_subscription_id"
	AttrBulkComplaintLevel                           = "bulk_complaint_level"
	AttrCategories                                   = "categories"
	AttrCc                                           = "cc"
	AttrClientVersion                                = "client_version"
	AttrCloudPlatform                                = "cloud_platform"
	AttrCloudPlatforms                               = "cloud_platforms"
	AttrCloudResource                                = "cloud_resource"
	AttrConfidenceLevel                              = "confidence_level"
	AttrConnectivityType                             = "connectivity_type"
	AttrConnectors                                   = "connectors"
	AttrContext                                      = "context"
	AttrCreatedProcessSessionId                      = "created_process_session_id"
	AttrDeliveryAction                               = "delivery_action"
	AttrDeliveryLocation                             = "delivery_location"
	AttrDetectionMethods                             = "detection_methods"
	AttrDeviceCategory                               = "device_category"
	AttrDeviceDynamicTags                            = "device_dynamic_tags"
	AttrDeviceManualTags                             = "device_manual_tags"
	AttrDeviceSubtype                                = "device_subtype"
	AttrDirection                                    = "direction"
	AttrDistributionList                             = "distribution_list"
	AttrEmailAction                                  = "email_action"
	AttrEmailActionPolicy                            = "email_action_policy"
	AttrEmailActionPolicyGuid                        = "email_action_policy_guid"
	AttrEmailClusterId                               = "email_cluster_id"
	AttrEmailDirection                               = "email_direction"
	AttrEmailLanguage                                = "email_language"
	AttrEmailSize                                    = "email_size"
	AttrEmailSubject                                 = "email_subject"
	AttrEntityType                                   = "entity_type"
	AttrEvidenceDirection                            = "evidence_direction"
	AttrEvidenceRole                                 = "evidence_role"
	AttrExchangeTransportRule                        = "exchange_transport_rule"
	AttrExclusionReason                              = "exclusion_reason"
	AttrExpires                                      = "expires"
	AttrExposureLevel                                = "exposure_level"
	AttrFileName                                     = "file_name"
	AttrFileOriginIp                                 = "file_origin_ip"
	AttrFileOriginReferrerUrl                        = "file_origin_referrer_url"
	AttrFileOriginUrl                                = "file_origin_url"
	AttrFileSize                                     = "file_size"
	AttrFolderPath                                   = "folder_path"
	AttrForwardingInformation                        = "forwarding_information"
	AttrGcpFullResourceName                          = "gcp_full_resource_name"
	AttrGeoAsn                                       = "geo_asn"
	AttrGeoCity                                      = "geo_city"
	AttrGeoCountry                                   = "geo_country"
	AttrGeoState                                     = "geo_state"
	AttrHardwareUuid                                 = "hardware_uuid"
	AttrHostDeviceId                                 = "host_device_id"
	AttrInitiatingProcessAccountDomain               = "initiating_process_account_domain"
	AttrInitiatingProcessAccountName                 = "initiating_process_account_name"
	AttrInitiatingProcessAccountObjectId             = "initiating_process_account_object_id"
	AttrInitiatingProcessAccountSid                  = "initiating_process_account_sid"
	AttrInitiatingProcessAccountUpn                  = "initiating_process_account_upn"
	AttrInitiatingProcessCommandLine                 = "initiating_process_command_line"
	AttrInitiatingProcessCreationTime                = "initiating_process_creation_time"
	AttrInitiatingProcessFileName                    = "initiating_process_file_name"
	AttrInitiatingProcessFileSize                    = "initiating_process_file_size"
	AttrInitiatingProcessFolderPath                  = "initiating_process_folder_path"
	AttrInitiatingProcessId                          = "initiating_process_id"
	AttrInitiatingProcessIntegrityLevel              = "initiating_process_integrity_level"
	AttrInitiatingProcessLogonId                     = "initiating_process_logon_id"
	AttrInitiatingProcessMd5                         = "initiating_process_md5"
	AttrInitiatingProcessParentCreationTime          = "initiating_process_parent_creation_time"
	AttrInitiatingProcessParentFileName              = "initiating_process_parent_file_name"
	AttrInitiatingProcessParentId                    = "initiating_process_parent_id"
	AttrInitiatingProcessRemoteSessionDeviceName     = "initiating_process_remote_session_device_name"
	AttrInitiatingProcessRemoteSessionIp             = "initiating_process_remote_session_ip"
	AttrInitiatingProcessSessionId                   = "initiating_process_session_id"
	AttrInitiatingProcessSha1                        = "initiating_process_sha1"
	AttrInitiatingProcessSha256                      = "initiating_process_sha256"
	AttrInitiatingProcessSignatureStatus             = "initiating_process_signature_status"
	AttrInitiatingProcessSignerType                  = "initiating_process_signer_type"
	AttrInitiatingProcessTokenElevation              = "initiating_process_token_elevation"
	AttrInitiatingProcessUniqueId                    = "initiating_process_unique_id"
	AttrInitiatingProcessVersionInfoCompanyName      = "initiating_process_version_info_company_name"
	AttrInitiatingProcessVersionInfoFileDescription  = "initiating_process_version_info_file_description"
	AttrInitiatingProcessVersionInfoInternalFileName = "initiating_process_version_info_internal_file_name"
	AttrInitiatingProcessVersionInfoOriginalFileName = "initiating_process_version_info_original_file_name"
	AttrInitiatingProcessVersionInfoProductName      = "initiating_process_version_info_product_name"
	AttrInitiatingProcessVersionInfoProductVersion   = "initiating_process_version_info_product_version"
	AttrInternetMessageId                            = "internet_message_id"
	AttrIsAzureAdJoined                              = "is_azure_ad_joined"
	AttrIsAzureInfoProtectionApplied                 = "is_azure_info_protection_applied"
	AttrIsExcluded                                   = "is_excluded"
	AttrIsFirstContact                               = "is_first_contact"
	AttrIsInitiatingProcessRemoteSession             = "is_initiating_process_remote_session"
	AttrIsInternetFacing                             = "is_internet_facing"
	AttrIsLocalAdmin                                 = "is_local_admin"
	AttrIsProcessRemoteSession                       = "is_process_remote_session"
	AttrIsTransient                                  = "is_transient"
	AttrJoinType                                     = "join_type"
	AttrLocalIp                                      = "local_ip"
	AttrLocalIpType                                  = "local_ip_type"
	AttrLocalPort                                    = "local_port"
	AttrLoggedOnUsers                                = "logged_on_users"
	AttrLogonId                                      = "logon_id"
	AttrLogonType                                    = "logon_type"
	AttrMachineGroup                                 = "machine_group"
	AttrMd5                                          = "md5"
	AttrMergedDeviceIds                              = "merged_device_ids"
	AttrMergedToDeviceId                             = "merged_to_device_id"
	AttrMitigationStatus                             = "mitigation_status"
	AttrNetworkMessageId                             = "network_message_id"
	AttrOauthApplicationId                           = "oauth_application_id"
	AttrOnboardingStatus                             = "onboarding_status"
	AttrOrgLevelAction                               = "org_level_action"
	AttrOrgLevelPolicy                               = "org_level_policy"
	AttrOsArchitecture                               = "os_architecture"
	AttrOsBuild                                      = "os_build"
	AttrOsBuildRevision                              = "os_build_revision"
	AttrOsDistribution                               = "os_distribution"
	AttrOsPlatform                                   = "os_platform"
	AttrOsVersionInfo                                = "os_version_info"
	AttrOverrideReason                               = "override_reason"
	AttrPermissionToAllowSender                      = "permission_to_allow_sender"
	AttrPermissionToBlockSender                      = "permission_to_block_sender"
	AttrPermissionToDelete                           = "permission_to_delete"
	AttrPermissionToDownload                         = "permission_to_download"
	AttrPermissionToPreview                          = "permission_to_preview"
	AttrPermissionToRelease                          = "permission_to_release"
	AttrPermissionToRequestRelease                   = "permission_to_request_release"
	AttrPermissionToViewHeader                       = "permission_to_view_header"
	AttrPreviousFileName                             = "previous_file_name"
	AttrPreviousFolderPath                           = "previous_folder_path"
	AttrPreviousRegistryKey                          = "previous_registry_key"
	AttrPreviousRegistryValueData                    = "previous_registry_value_data"
	AttrPreviousRegistryValueName                    = "previous_registry_value_name"
	AttrProcessCommandLine                           = "process_command_line"
	AttrProcessCreationTime                          = "process_creation_time"
	AttrProcessId                                    = "process_id"
	AttrProcessIntegrityLevel                        = "process_integrity_level"
	AttrProcessRemoteSessionDeviceName               = "process_remote_session_device_name"
	AttrProcessRemoteSessionIp                       = "process_remote_session_ip"
	AttrProcessTokenElevation                        = "process_token_elevation"
	AttrProcessUniqueId                              = "process_unique_id"
	AttrProcessVersionInfoCompanyName                = "process_version_info_company_name"
	AttrProcessVersionInfoFileDescription            = "process_version_info_file_description"
	AttrProcessVersionInfoInternalFileName           = "process_version_info_internal_file_name"
	AttrProcessVersionInfoOriginalFileName           = "process_version_info_original_file_name"
	AttrProcessVersionInfoProductName                = "process_version_info_product_name"
	AttrProcessVersionInfoProductVersion             = "process_version_info_product_version"
	AttrProtocol                                     = "protocol"
	AttrPublicIp                                     = "public_ip"
	AttrQuarantineType                               = "quarantine_type"
	AttrReceivedTime                                 = "received_time"
	AttrRecipientAddress                             = "recipient_address"
	AttrRecipientCount                               = "recipient_count"
	AttrRecipientDomain                              = "recipient_domain"
	AttrRecipientEmailAddress                        = "recipient_email_address"
	AttrRecipientObjectId                            = "recipient_object_id"
	AttrRecipientTag                                 = "recipient_tag"
	AttrRegistryDeviceTag                            = "registry_device_tag"
	AttrRegistryKey                                  = "registry_key"
	AttrRegistryValueData                            = "registry_value_data"
	AttrRegistryValueName                            = "registry_value_name"
	AttrRegistryValueType                            = "registry_value_type"
	AttrReleaseStatus                                = "release_status"
	AttrReleased                                     = "released"
	AttrReleasedBy                                   = "released_by"
	AttrReleasedCount                                = "released_count"
	AttrRemoteDeviceName                             = "remote_device_name"
	AttrRemoteIp                                     = "remote_ip"
	AttrRemoteIpType                                 = "remote_ip_type"
	AttrRemotePort                                   = "remote_port"
	AttrRemoteUrl                                    = "remote_url"
	AttrReportId                                     = "report_id"
	AttrReported                                     = "reported"
	AttrRequestAccountDomain                         = "request_account_domain"
	AttrRequestAccountName                           = "request_account_name"
	AttrRequestAccountSid                            = "request_account_sid"
	AttrRequestProtocol                              = "request_protocol"
	AttrRequestSourceIp                              = "request_source_ip"
	AttrRequestSourcePort                            = "request_source_port"
	AttrSenderAddress                                = "sender_address"
	AttrSenderDisplayName                            = "sender_display_name"
	AttrSenderFromAddress                            = "sender_from_address"
	AttrSenderFromDomain                             = "sender_from_domain"
	AttrSenderIpv4                                   = "sender_ipv4"
	AttrSenderIpv6                                   = "sender_ipv6"
	AttrSenderMailFromAddress                        = "sender_mail_from_address"
	AttrSenderMailFromDomain                         = "sender_mail_from_domain"
	AttrSenderObjectId                               = "sender_object_id"
	AttrSensitivityLabel                             = "sensitivity_label"
	AttrSensitivitySubLabel                          = "sensitivity_sub_label"
	AttrSensorHealthState                            = "sensor_health_state"
	AttrSha1                                         = "sha1"
	AttrSha256                                       = "sha256"
	AttrShareName                                    = "share_name"
	AttrSize                                         = "size"
	AttrSubject                                      = "subject"
	AttrSubscriptionId                               = "subscription_id"
	AttrSystemReleased                               = "system_released"
	AttrTagName                                      = "tag_name"
	AttrThreatClassification                         = "threat_classification"
	AttrThreatFamily                                 = "threat_family"
	AttrThreatNames                                  = "threat_names"
	AttrThreatTypes                                  = "threat_types"
	AttrTo                                           = "to"
	AttrUrlCount                                     = "url_count"
	AttrUserLevelAction                              = "user_level_action"
	AttrUserLevelPolicy                              = "user_level_policy"
	AttrVendor                                       = "vendor"

	// Deferred-companion tables (#106 tail: devicenetworkinfo, devicefilecertificateinfo,
	// emailurlinfo, emailattachmentinfo, identitylogonevents, deviceimageloadevents,
	// identityinfo, cloudappevents).
	AttrAccountDisplayName              = "account_display_name"
	AttrAccountId                       = "account_id"
	AttrAccountType                     = "account_type"
	AttrActivityObjects                 = "activity_objects"
	AttrAddress                         = "address"
	AttrAppInstanceId                   = "app_instance_id"
	AttrAuditSource                     = "audit_source"
	AttrBlastRadius                     = "blast_radius"
	AttrCertificateCountersignatureTime = "certificate_countersignature_time"
	AttrCertificateCreationTime         = "certificate_creation_time"
	AttrCertificateExpirationTime       = "certificate_expiration_time"
	AttrCertificateSerialNumber         = "certificate_serial_number"
	AttrChangeSource                    = "change_source"
	AttrCity                            = "city"
	AttrCloudSid                        = "cloud_sid"
	AttrCompanyName                     = "company_name"
	AttrConnectedNetworks               = "connected_networks"
	AttrCountry                         = "country"
	AttrCountryCode                     = "country_code"
	AttrCriticalityLevel                = "criticality_level"
	AttrCrlDistributionPointUrls        = "crl_distribution_point_urls"
	AttrDefaultGateways                 = "default_gateways"
	AttrDeletedDateTime                 = "deleted_date_time"
	AttrDepartment                      = "department"
	AttrDestinationDeviceName           = "destination_device_name"
	AttrDestinationIpAddress            = "destination_ip_address"
	AttrDestinationPort                 = "destination_port"
	AttrDistinguishedName               = "distinguished_name"
	AttrDnsAddresses                    = "dns_addresses"
	AttrEmailAddress                    = "email_address"
	AttrEmployeeId                      = "employee_id"
	AttrFileExtension                   = "file_extension"
	AttrFileType                        = "file_type"
	AttrGivenName                       = "given_name"
	AttrIdentityEnvironment             = "identity_environment"
	AttrIdentityId                      = "identity_id"
	AttrIpAddresses                     = "ip_addresses"
	AttrIpCategory                      = "ip_category"
	AttrIpTags                          = "ip_tags"
	AttrIpv4Dhcp                        = "ipv4_dhcp"
	AttrIpv6Dhcp                        = "ipv6_dhcp"
	AttrIsAccountEnabled                = "is_account_enabled"
	AttrIsAdminOperation                = "is_admin_operation"
	AttrIsAnonymousProxy                = "is_anonymous_proxy"
	AttrIsClickedThrough                = "is_clicked_through"
	AttrIsExternalUser                  = "is_external_user"
	AttrIsImpersonated                  = "is_impersonated"
	AttrIsRootSignerMicrosoft           = "is_root_signer_microsoft"
	AttrIsSigned                        = "is_signed"
	AttrIsp                             = "isp"
	AttrIssuerHash                      = "issuer_hash"
	AttrJobTitle                        = "job_title"
	AttrLastSeenForUser                 = "last_seen_for_user"
	AttrMacAddress                      = "mac_address"
	AttrManager                         = "manager"
	AttrNetworkAdapterName              = "network_adapter_name"
	AttrNetworkAdapterStatus            = "network_adapter_status"
	AttrNetworkAdapterType              = "network_adapter_type"
	AttrNetworkAdapterVendor            = "network_adapter_vendor"
	AttrOauthAppId                      = "oauth_app_id"
	AttrObjectName                      = "object_name"
	AttrOnPremObjectId                  = "on_prem_object_id"
	AttrOnPremSid                       = "on_prem_sid"
	AttrOtherMailAddresses              = "other_mail_addresses"
	AttrPhone                           = "phone"
	AttrPort                            = "port"
	AttrPrivilegedEntraPimRoles         = "privileged_entra_pim_roles"
	AttrRawEventData                    = "raw_event_data"
	AttrRiskLevelDetails                = "risk_level_details"
	AttrSessionData                     = "session_data"
	AttrSignatureType                   = "signature_type"
	AttrSigner                          = "signer"
	AttrSignerHash                      = "signer_hash"
	AttrSipProxyAddress                 = "sip_proxy_address"
	AttrSourceId                        = "source_id"
	AttrSourceProvider                  = "source_provider"
	AttrSourceProviders                 = "source_providers"
	AttrSurname                         = "surname"
	AttrTags                            = "tags"
	AttrTargetAccountDisplayName        = "target_account_display_name"
	AttrTargetDeviceName                = "target_device_name"
	AttrTunnelType                      = "tunnel_type"
	AttrUncommonForUser                 = "uncommon_for_user"
	AttrUrl                             = "url"
	AttrUrlChain                        = "url_chain"
	AttrUrlChainId                      = "url_chain_id"
	AttrUrlChainPosition                = "url_chain_position"
	AttrUrlDomain                       = "url_domain"
	AttrUrlLocation                     = "url_location"
	AttrUserAccountControl              = "user_account_control"
	AttrUserAgentTags                   = "user_agent_tags"

	// #241 tables: behaviorinfo/behaviorentities (Defender behavior analytics,
	// the alert-info+evidence shape joined by BehaviorId) and
	// messageevents/messageurlinfo (Teams message hygiene, the email+emailurl
	// shape joined by TeamsMessageId).
	AttrBehaviorId         = "behavior_id"
	AttrDataSources        = "data_sources"
	AttrDetailedEntityRole = "detailed_entity_role"
	AttrEndTime            = "end_time"
	AttrEntityRole         = "entity_role"
	AttrGroupId            = "group_id"
	AttrGroupName          = "group_name"
	AttrIsExternalThread   = "is_external_thread"
	AttrIsOwnedThread      = "is_owned_thread"
	AttrLastEditedTime     = "last_edited_time"
	AttrMessageId          = "message_id"
	AttrMessageSubtype     = "message_subtype"
	AttrMessageType        = "message_type"
	AttrMessageVersion     = "message_version"
	AttrParentMessageId    = "parent_message_id"
	AttrRecipientDetails   = "recipient_details"
	AttrSafetyTip          = "safety_tip"
	AttrSenderEmailAddress = "sender_email_address"
	AttrSenderType         = "sender_type"
	AttrStartTime          = "start_time"
	AttrTeamsMessageId     = "teams_message_id"
	AttrThreadId           = "thread_id"
	AttrThreadName         = "thread_name"
	AttrThreadSubType      = "thread_sub_type"
	AttrThreadType         = "thread_type"
)

Attribute keys INTRODUCED by defender.* collectors — the Microsoft Defender XDR advanced-hunting tables (#106). "Introduced by", not "used only by": the mail keys defender.quarantine coined off the Exchange Online admin API (internet_message_id, received_time, sender_address, recipient_address, subject, size) are reused verbatim by m365.message_trace (#254), which is exactly what makes the two joinable in LogQL with no translation table. Do not re-coin one here for another domain — the registry's no-duplicate-values gate would reject it anyway.

One key per advanced-hunting column, plus a few derived keys (geo_* promoted from an AlertEvidence Ip entity's Location). Shared keys these tables also emit (id, device_id, device_name, severity, ... failure_reason, ip_address) are reused from the other attrs_*.go files, never redeclared. Every key is a semconv.Attr* constant (Gate B); no value duplicates another constant (Gate A).

View Source
const (
	// AttrProtection is the bounded protection-name label on
	// defender.mdo.protection_enabled (zap, spam_zap, safe_links_email, ...): a
	// small closed set, so it is a legal metric label.
	AttrProtection = "protection"

	// String action/threshold-name fields carried on the defender.mdo_policy twin.
	AttrRecommendedPolicyType               = "recommended_policy_type"
	AttrSpamAction                          = "spam_action"
	AttrHighConfidenceSpamAction            = "high_confidence_spam_action"
	AttrPhishSpamAction                     = "phish_spam_action"
	AttrHighConfidencePhishAction           = "high_confidence_phish_action"
	AttrBulkSpamAction                      = "bulk_spam_action"
	AttrFileTypeAction                      = "file_type_action"
	AttrAuthenticationFailAction            = "authentication_fail_action"
	AttrDmarcRejectAction                   = "dmarc_reject_action"
	AttrDmarcQuarantineAction               = "dmarc_quarantine_action"
	AttrMailboxIntelligenceProtectionAction = "mailbox_intelligence_protection_action"
	AttrTargetedUserProtectionAction        = "targeted_user_protection_action"
	AttrTargetedDomainProtectionAction      = "targeted_domain_protection_action"
	AttrSafeAttachmentAction                = "safe_attachment_action"

	// Numeric threshold/retention fields on the defender.mdo_policy twin.
	AttrBulkThreshold             = "bulk_threshold"
	AttrPhishThresholdLevel       = "phish_threshold_level"
	AttrQuarantineRetentionPeriod = "quarantine_retention_period"

	// Boolean posture toggles on the defender.mdo_policy twin. Emitted even when
	// false (#114): false is the answer an operator filters for (a protection
	// switched off on a policy that supports it).
	AttrZapEnabled                   = "zap_enabled"
	AttrSpamZapEnabled               = "spam_zap_enabled"
	AttrPhishZapEnabled              = "phish_zap_enabled"
	AttrFileFilterEnabled            = "file_filter_enabled"
	AttrSpoofIntelligenceEnabled     = "spoof_intelligence_enabled"
	AttrMailboxIntelligenceEnabled   = "mailbox_intelligence_enabled"
	AttrSafeDocsEnabled              = "safe_docs_enabled"
	AttrSafeLinksEmailEnabled        = "safe_links_email_enabled"
	AttrSafeLinksTeamsEnabled        = "safe_links_teams_enabled"
	AttrSafeLinksOfficeEnabled       = "safe_links_office_enabled"
	AttrSafeAttachmentsEnabled       = "safe_attachments_enabled"
	AttrScanUrlsEnabled              = "scan_urls_enabled"
	AttrAllowClickThrough            = "allow_click_through"
	AttrDeliverMessageAfterScan      = "deliver_message_after_scan"
	AttrEnableForInternalSenders     = "enable_for_internal_senders"
	AttrEnableTargetedUserProtection = "enable_targeted_user_protection"
	AttrEnableTargetedDomainsProt    = "enable_targeted_domains_protection"
	AttrAllowSafeDocsOpen            = "allow_safe_docs_open"
	AttrEnableAtpForSpoTeamsOdb      = "enable_atp_for_spo_teams_odb"
	AttrHonorDmarcPolicy             = "honor_dmarc_policy"
)

MDO policy-posture keys (#250) — defender.mdo_policies, the Microsoft Defender for Office 365 policy collector reached over the Exchange Online admin API (EXO registration path). policy_type/policy_name are reused from attrs_intune.go, name from attrs_purview.go, is_default from attrs_entra.go and enabled/action from the other domain files; only the keys below are new. Every key is a semconv.Attr* constant (Gate B) with a value that duplicates no other constant (Gate A).

View Source
const (
	// defender.vulnerabilities — per-device software-vulnerability rows, joined
	// server-side to the KB table for CVSS/EPSS/exploit availability.
	AttrSoftwareVendor            = "software_vendor"
	AttrSoftwareName              = "software_name"
	AttrSoftwareVersion           = "software_version"
	AttrCveId                     = "cve_id"
	AttrExploitAvailable          = "exploit_available"
	AttrRecommendedSecurityUpdate = "recommended_security_update"
	AttrCveMitigationStatus       = "cve_mitigation_status"
	AttrCvssScore                 = "cvss_score"
	AttrEpssScore                 = "epss_score"
	AttrCvssVector                = "cvss_vector"
	AttrPublishedDate             = "published_date"

	// defender.secure_config — per-device secure-configuration assessment rows.
	AttrConfigurationCategory    = "configuration_category"
	AttrConfigurationSubcategory = "configuration_subcategory"
	AttrConfigurationId          = "configuration_id"
	AttrConfigurationImpact      = "configuration_impact"

	// defender.software_inventory — per-device installed-software rows.
	AttrEndOfSupportStatus = "end_of_support_status"
	AttrEndOfSupportDate   = "end_of_support_date"
	AttrProductCodeCpe     = "product_code_cpe"
)

Advanced-hunting DeviceTvm* attributes (#249) — the threat-and-vulnerability- management posture reached over the Graph runHuntingQuery API. Reused from other domains where a key already exists: AttrOsPlatform (above), AttrOsVersion and AttrDeviceId/AttrDeviceName (intune), AttrIsCompliant (entra), AttrSeverity (shared). Every value below is the snake_case of a column read verbatim off a live query result, never a docs field.

View Source
const (
	AttrPrivilegeLevel        = "privilege_level"
	AttrAppStatus             = "app_status"
	AttrAppOrigin             = "app_origin"
	AttrAdminConsented        = "admin_consented"
	AttrRiskScore             = "risk_score"
	AttrConsentedUsersCount   = "consented_users_count"
	AttrAppOwnerTenantId      = "app_owner_tenant_id"
	AttrLastUsedTime          = "last_used_time"
	AttrAddedOnTime           = "added_on_time"
	AttrVerifiedPublisherName = "verified_publisher_name"
	AttrVerifiedPublisherId   = "verified_publisher_id"
	AttrIsVerifiedPublisher   = "is_verified_publisher"
	AttrPermissionValues      = "permission_values"
	AttrPermissionsCount      = "permissions_count"
)

OAuthAppInfo attributes (#252) — the Defender for Cloud Apps OAuth-app inventory, reached over the advanced-hunting query API. Reused where a key exists: AttrOauthAppId (above), AttrAppName (intune), AttrServicePrincipalId (entra). Every value is the snake_case of a column read verbatim off a live query result.

View Source
const (
	AttrEntryError            = "entry_error"
	AttrEntryValue            = "entry_value"
	AttrEntryValueHash        = "entry_value_hash"
	AttrLastUsedDate          = "last_used_date"
	AttrListSubtype           = "list_subtype"
	AttrListType              = "list_type"
	AttrModifiedBy            = "modified_by"
	AttrNotes                 = "notes"
	AttrObjectState           = "object_state"
	AttrRemoveAfterDays       = "remove_after_days"
	AttrSendingInfrastructure = "sending_infrastructure"
	AttrSpoofedUser           = "spoofed_user"
	AttrSpoofType             = "spoof_type"
	AttrSubmissionId          = "submission_id"
	AttrSysManaged            = "sys_managed"
)

Tenant Allow/Block List attributes (#250) — defender.allow_block_list, the standing-holes-past-mail-security collector reached over the Exchange Online admin API (EXO registration path). Reused where a key already exists: AttrAction (above), AttrId and AttrExpirationDateTime and AttrExpiryBucket (shared), AttrCreatedDateTime (entra), AttrLastModifiedDateTime (m365). Only list_type, list_subtype and spoof_type are ever metric labels — all three are bounded value sets fixed by the API, not by tenant size. Every other key below is per-entry data and appears on the log twin only (#112). Every value is the snake_case of a column read verbatim off a live record.

View Source
const (
	AttrActivityDisplayName               = "activity_display_name"
	AttrAgentDisplayName                  = "agent_display_name"
	AttrAgentId                           = "agent_id"
	AttrAgreement                         = "agreement"
	AttrAlertCount                        = "alert_count"
	AttrAlertIds                          = "alert_ids"
	AttrApiVersion                        = "api_version"
	AttrAppDisplayName                    = "app_display_name"
	AttrAppObjectId                       = "app_object_id"
	AttrAppRole                           = "app_role"
	AttrAppRoleId                         = "app_role_id"
	AttrAssignedTo                        = "assigned_to"
	AttrAssignmentType                    = "assignment_type"
	AttrAuthenticationType                = "authentication_type"
	AttrBlueprintId                       = "blueprint_id"
	AttrCallerIpAddress                   = "caller_ip_address"
	AttrChangeId                          = "change_id"
	AttrClassification                    = "classification"
	AttrClientAppUsed                     = "client_app_used"
	AttrClientId                          = "client_id"
	AttrClientRequestId                   = "client_request_id"
	AttrConditionalAccessStatus           = "conditional_access_status"
	AttrConflictingValue                  = "conflicting_value"
	AttrConsentType                       = "consent_type"
	AttrContextId                         = "context_id"
	AttrCreatedDateTime                   = "created_date_time"
	AttrCreatedTime                       = "created_time"
	AttrCredentialDisplayName             = "credential_display_name"
	AttrCredentialOrigin                  = "credential_origin"
	AttrCredentialType                    = "credential_type"
	AttrCustomKeyIdentifier               = "custom_key_identifier"
	AttrCustomTags                        = "custom_tags"
	AttrCycleId                           = "cycle_id"
	AttrDetectionSource                   = "detection_source"
	AttrDetectionTimingType               = "detection_timing_type"
	AttrDetermination                     = "determination"
	AttrDurationMs                        = "duration_ms"
	AttrEndDateTime                       = "end_date_time"
	AttrEvidenceCount                     = "evidence_count"
	AttrGrantedScope                      = "granted_scope"
	AttrGroupType                         = "group_type"
	AttrIdentityProvider                  = "identity_provider"
	AttrIdentityType                      = "identity_type"
	AttrIncidentId                        = "incident_id"
	AttrInitiatorAppDisplayName           = "initiator_app_display_name"
	AttrInitiatorAppId                    = "initiator_app_id"
	AttrInitiatorServicePrincipalId       = "initiator_service_principal_id"
	AttrInitiatorUserId                   = "initiator_user_id"
	AttrInitiatorUserPrincipalName        = "initiator_user_principal_name"
	AttrIpAddress                         = "ip_address"
	AttrIsAdmin                           = "is_admin"
	AttrIsAdminManaged                    = "is_admin_managed"
	AttrIsDefault                         = "is_default"
	AttrIsEnabled                         = "is_enabled"
	AttrIsInitial                         = "is_initial"
	AttrIsProcessing                      = "is_processing"
	AttrIsReplay                          = "is_replay"
	AttrIsRoot                            = "is_root"
	AttrIsTrusted                         = "is_trusted"
	AttrIsVerified                        = "is_verified"
	AttrJobId                             = "job_id"
	AttrKeyId                             = "key_id"
	AttrKeyType                           = "key_type"
	AttrLastNonInteractiveSignInDateTime  = "last_non_interactive_sign_in_date_time"
	AttrLastNonInteractiveSignInRequestId = "last_non_interactive_sign_in_request_id"
	AttrLastSignInDateTime                = "last_sign_in_date_time"
	AttrLastSignInRequestId               = "last_sign_in_request_id"
	AttrLastSuccessfulSignInDateTime      = "last_successful_sign_in_date_time"
	AttrLastSuccessfulSignInRequestId     = "last_successful_sign_in_request_id"
	AttrLastUpdateTime                    = "last_update_time"
	AttrLastUpdated                       = "last_updated"
	AttrLocation                          = "location"
	AttrLocationCity                      = "location_city"
	AttrLocationCountryOrRegion           = "location_country_or_region"
	AttrLocationLatitude                  = "location_latitude"
	AttrLocationLongitude                 = "location_longitude"
	AttrLocationState                     = "location_state"
	AttrLoggedByService                   = "logged_by_service"
	AttrMailEnabled                       = "mail_enabled"
	AttrMembershipType                    = "membership_type"
	AttrMethod                            = "method"
	AttrMethodsRegistered                 = "methods_registered"
	AttrMfaCapable                        = "mfa_capable"
	AttrMfaRegistered                     = "mfa_registered"
	AttrMitreTechniques                   = "mitre_techniques"
	AttrNearPurge                         = "near_purge"
	AttrObjectType                        = "object_type"
	AttrOccurredDateTime                  = "occurred_date_time"
	AttrOperationId                       = "operation_id"
	AttrOperationType                     = "operation_type"
	AttrOwnerType                         = "owner_type"
	AttrPasswordlessCapable               = "passwordless_capable"
	AttrPermanent                         = "permanent"
	AttrPrincipalAppId                    = "principal_app_id"
	AttrPrincipalDisplayName              = "principal_display_name"
	AttrPrincipalId                       = "principal_id"
	AttrPrincipalType                     = "principal_type"
	AttrPrincipalUserPrincipalName        = "principal_user_principal_name"
	AttrPriorityScore                     = "priority_score"
	AttrPrivilege                         = "privilege"
	AttrPropertyCausingError              = "property_causing_error"
	AttrProviderAlertId                   = "provider_alert_id"
	AttrProvisioningAction                = "provisioning_action"
	AttrRecommendation                    = "recommendation"
	AttrRequestId                         = "request_id"
	AttrNormalizedPath                    = "normalized_path"
	AttrRequestMethod                     = "request_method"
	AttrRequestUri                        = "request_uri"
	AttrResourceDisplayName               = "resource_display_name"
	AttrResourceId                        = "resource_id"
	AttrResourceLabel                     = "resource_label"
	AttrResponseSizeBytes                 = "response_size_bytes"
	AttrResponseStatusClass               = "response_status_class"
	AttrResponseStatusCode                = "response_status_code"
	AttrResult                            = "result"
	AttrResultDescription                 = "result_description"
	AttrResultReason                      = "result_reason"
	AttrResultStatusCode                  = "result_status_code"
	AttrRiskDetail                        = "risk_detail"
	AttrRiskEventType                     = "risk_event_type"
	AttrRiskEvidence                      = "risk_evidence"
	AttrRiskLastUpdated                   = "risk_last_updated"
	AttrRiskLevel                         = "risk_level"
	AttrRiskLevelDuringSignIn             = "risk_level_during_sign_in"
	AttrRiskState                         = "risk_state"
	AttrRoleId                            = "role_id"
	AttrRoleName                          = "role_name"
	AttrRoles                             = "roles"
	AttrScope                             = "scope"
	AttrScopes                            = "scopes"
	AttrSecurityEnabled                   = "security_enabled"
	AttrServicePrincipalId                = "service_principal_id"
	AttrServicePrincipalName              = "service_principal_name"
	AttrServicePrincipalObjectId          = "service_principal_object_id"
	AttrServicePrincipalType              = "service_principal_type"
	AttrServiceSource                     = "service_source"
	AttrSignInActivityId                  = "sign_in_activity_id"
	AttrSignInEventTypes                  = "sign_in_event_types"
	AttrSku                               = "sku"
	AttrSourceIdentityDisplayName         = "source_identity_display_name"
	AttrSourceIdentityId                  = "source_identity_id"
	AttrSsprCapable                       = "sspr_capable"
	AttrSsprEnabled                       = "sspr_enabled"
	AttrSsprRegistered                    = "sspr_registered"
	AttrStatusErrorCode                   = "status_error_code"
	AttrStatusFailureReason               = "status_failure_reason"
	AttrStatusInfo                        = "status_info"
	AttrSupportedServices                 = "supported_services"
	AttrSystemPreferredAuthEnabled        = "system_preferred_auth_enabled"
	AttrSystemPreferredAuthMethods        = "system_preferred_auth_methods"
	AttrSystemTags                        = "system_tags"
	AttrTargetDisplayNames                = "target_display_names"
	AttrTargetIdentityDisplayName         = "target_identity_display_name"
	AttrTargetIdentityId                  = "target_identity_id"
	AttrTargetResourceCount               = "target_resource_count"
	AttrTenantType                        = "tenant_type"
	AttrThresholdDays                     = "threshold_days"
	AttrTitle                             = "title"
	AttrTokenIssuedAt                     = "token_issued_at"
	AttrTokenIssuerType                   = "token_issuer_type"
	AttrTrustType                         = "trust_type"
	AttrUserAgent                         = "user_agent"
	AttrUserPreferredSecondaryMethod      = "user_preferred_secondary_method"
	AttrUserPrincipalObjectId             = "user_principal_object_id"
	AttrWids                              = "wids"
	AttrWorkloadNamespace                 = "workload_namespace"
)

Attribute keys used only by entra.* collectors.

View Source
const (
	AttrAccountEnabled        = "account_enabled"
	AttrIsCompliant           = "is_compliant"
	AttrIsManaged             = "is_managed"
	AttrOnPremisesSyncEnabled = "on_premises_sync_enabled"
)

Attribute keys passed indirectly — via a helper-func param (devices.boolSnapshot) or an axis-struct field (users) rather than as a literal at the emit site — so the #161 seed extraction, which scanned only direct setX/attrs[]/Attrs{} forms, missed them. Added in the migration so no bare attribute-key literal remains.

View Source
const (
	// entra.application / federated-identity-credential (#244): app-ownership and
	// the trust-edge fields credential_expiry cannot see.
	AttrHasOwner            = "has_owner"
	AttrSignInAudience      = "sign_in_audience"
	AttrIssuerHost          = "issuer_host"
	AttrOwnerCount          = "owner_count"
	AttrOwnerPrincipalNames = "owner_principal_names"
	AttrCredentialName      = "credential_name"
	AttrAudiences           = "audiences"

	// entra.tenant_policy twin (#245): tenant posture fields the bounded 0/1
	// setting gauge cannot carry.
	AttrAllowInvitesFrom            = "allow_invites_from"
	AttrGuestUserRoleId             = "guest_user_role_id"
	AttrAdminConsentReviewerCount   = "admin_consent_reviewer_count"
	AttrAdminConsentRequestDuration = "admin_consent_request_duration_days"
	AttrPermissionGrantPolicies     = "permission_grant_policies"
	AttrAppPasswordCredsRestricted  = "app_password_credentials_restricted"

	// entra.pim_role_policy (#242): PIM role-activation policy requirements.
	AttrRoleDefinitionId          = "role_definition_id"
	AttrRequirement               = "requirement"
	AttrCaller                    = "caller"
	AttrIsOrganizationDefault     = "is_organization_default"
	AttrApprovalRequired          = "approval_required"
	AttrApprovalStageTimeoutDays  = "approval_stage_timeout_days"
	AttrActivationEnabledRules    = "activation_enabled_rules"
	AttrActivationMaxDuration     = "activation_max_duration"
	AttrAssignmentExpiryRequired  = "assignment_expiry_required"
	AttrAssignmentMaxDuration     = "assignment_max_duration"
	AttrEligibilityExpiryRequired = "eligibility_expiry_required"
	AttrEligibilityMaxDuration    = "eligibility_max_duration"
	AttrAuthContextEnabled        = "auth_context_enabled"
	AttrAuthContextClaim          = "auth_context_claim"

	// entra.gsa Global Secure Access posture twins (#239). The forwarding-profile
	// and filtering-policy twins carry per-entity config detail the bounded gauges
	// cannot. id/name/state/action/version/priority reuse the shared/domain
	// constants (AttrId, AttrName, AttrState, AttrAction, AttrVersion,
	// AttrPriority); these four are genuinely new.
	AttrTrafficForwardingType = "traffic_forwarding_type"
	AttrIsCustomProfile       = "is_custom_profile"
	AttrClientFallbackAction  = "client_fallback_action"
	AttrAssociationCount      = "association_count"

	AttrControlName          = "control_name"
	AttrScore                = "score"
	AttrScoreInPercentage    = "score_in_percentage"
	AttrImplementationStatus = "implementation_status"
	AttrControlCount         = "control_count"
	AttrControlTotal         = "control_total"
	AttrLastSynced           = "last_synced"
	AttrMaxScore             = "max_score"
	AttrTier                 = "tier"
	AttrRank                 = "rank"
	AttrThreats              = "threats"
	AttrActionUrl            = "action_url"
	AttrDeprecated           = "deprecated"
	AttrImplementationCost   = "implementation_cost"
	AttrUserImpact           = "user_impact"
	AttrScoreComparisonBasis = "basis"
)

Attribute keys for the entra.secure_score control twins (#243). The per-control state twin (entra.secure_score_control) carries the tenant's current per-control assessment from controlScores; the catalog twin (entra.secure_score_control_profile) carries the bounded control-profile metadata. Both are LOGS — per-control detail belongs on the twin, never a metric label (#114). control_category reuses AttrCategory; the profile's service/action_type reuse AttrService/AttrActionType; the profile id reuses AttrId.

View Source
const (
	// AttrCreatedById is createdBy.id — the object id of the principal that
	// created the review. On the live tenant this is a SERVICE PRINCIPAL, not a
	// user, which is why the id is carried even when the name is not: it is the
	// only part of createdBy that is ever populated there.
	AttrCreatedById = "created_by_id"
	// AttrCreatedByUserPrincipalName is createdBy.userPrincipalName. Empty on
	// the live wire, so omitted; see the family note above.
	AttrCreatedByUserPrincipalName = "created_by_user_principal_name"
	// AttrCreatedByType is createdBy.type. Null on the live wire, so omitted.
	AttrCreatedByType = "created_by_type"

	// AttrDescriptionForReviewers is descriptionForReviewers — the text shown to
	// the reviewer, distinct from AttrDescriptionForAdmins. Empty on the live
	// wire, so omitted.
	AttrDescriptionForReviewers = "description_for_reviewers"
)
  • Every key here is LOG-ONLY
  • Reused rather than re-coined
  • The createdBy family, and why it is three keys and not one

Attribute keys introduced by entra.access_reviews (#260), the Entra ID Governance access-review DEFINITION inventory.

Every key here is LOG-ONLY

The collector's only metric is entra.access_reviews.total, labeled by `status` alone (the existing AttrStatus). Nothing below may become a metric label: a review definition is an entity, so a series keyed by its id, its display name, its scope queries or its reviewer list is the #112 failure exactly. The bounded count answers "how many reviews are in each state"; the twin answers "which one, scoped to what, reviewed by whom".

Reused rather than re-coined

Seven of the record's fields already have a constant, and those are REUSED:

id                      -> AttrId
displayName             -> AttrDisplayName
status                  -> AttrStatus
createdDateTime         -> AttrCreatedDateTime
lastModifiedDateTime    -> AttrLastModifiedDateTime
descriptionForAdmins    -> AttrDescriptionForAdmins   (coined by purview)
createdBy.displayName   -> AttrCreatedBy              (coined by m365; the
                           same meaning — who created this object, in
                           human-readable form)

The registry's no-duplicate-values gate enforces this from the other side: a second constant carrying "display_name" is a build failure.

The createdBy family, and why it is three keys and not one

createdBy arrives as a userIdentity OBJECT, and on the live wire its `displayName` and `userPrincipalName` are EMPTY STRINGS while `id` is set (live-measured 2026-07-24, #260) — Graph did not resolve the principal, it did not merely omit it. The three parts therefore travel as three keys, each set through telemetry.SetStr so an empty one is OMITTED rather than stamped blank. A blank user_principal_name on a twin reads as "this review has no creator", which is false; an absent one reads as "not resolved", which is true.

View Source
const (
	// AttrScopeODataType is the scope's @odata.type, prefix-stripped
	// (e.g. "principalResourceMembershipsScope").
	//
	// Deliberately NOT "scope_type", which intune.rbac already uses for a
	// genuinely different thing: the wire's own `scopeType` enum on a role
	// assignment (`allDevicesAndLicensedUsers` and friends). Both were coined as
	// "scope_type" independently and collided at compile time — which was lucky,
	// because the alternative is one attribute key meaning two things depending
	// on which collector wrote the record, and a query filtering on it silently
	// matching the wrong shape. That is the tpm_version collision from #199,
	// caught earlier this time.
	AttrScopeODataType = "scope_odata_type"
	// AttrScopeQuery is the single `query` a plain accessReviewQueryScope holds.
	AttrScopeQuery = "scope_query"
	// AttrScopePrincipalQueries is principalResourceMembershipsScope.principalScopes'
	// queries — WHO is being reviewed (e.g. "/v1.0/users").
	AttrScopePrincipalQueries = "scope_principal_queries"
	// AttrScopeResourceQueries is principalResourceMembershipsScope.resourceScopes'
	// queries — WHAT access is being reviewed (e.g. a directory roleDefinition
	// URL, which is how a "review the Global Administrators" review identifies
	// itself).
	AttrScopeResourceQueries = "scope_resource_queries"
)

The scope family. `scope` is POLYMORPHIC: its concrete shape is named by an `@odata.type` discriminator, and the collector switches on that rather than probing for fields it hopes are there. AttrScopeODataType carries the discriminator with the "#microsoft.graph." prefix stripped; the remaining three carry the query strings the chosen shape actually holds, so which of them is present is itself a function of the scope type.

These are deliberately NOT the existing AttrScope/AttrScopes, which carry OAuth permission scopes elsewhere in this codebase — an unrelated meaning.

View Source
const (
	// AttrReviewerCount is len(reviewers).
	AttrReviewerCount = "reviewer_count"
	// AttrReviewerQueries is each reviewer's `query` — a Graph URL naming the
	// principal (e.g. "/v1.0/users/{guid}"), which is the identity form this
	// endpoint returns. There is no separate id field to prefer.
	AttrReviewerQueries = "reviewer_queries"
	// AttrFallbackReviewerCount is len(fallbackReviewers).
	//
	// There is NO backup_reviewer_count key, on purpose. `backupReviewers` is
	// returned by the BETA endpoint and NOT by v1.0 (live-measured 2026-07-24,
	// #260 — both were probed), and this collector reads v1.0. Emitting a zero
	// for a field the chosen endpoint never sends would publish a fabricated
	// fact, which is the failure mode CLAUDE.md's "absent field is not a
	// sentinel" rule exists to stop.
	AttrFallbackReviewerCount = "fallback_reviewer_count"
	// AttrAdditionalNotificationRecipientCount is
	// len(additionalNotificationRecipients).
	AttrAdditionalNotificationRecipientCount = "additional_notification_recipient_count"
	// AttrStageCount is len(stageSettings) — how many stages a multi-stage
	// review runs. Zero on the live wire (a single-stage review).
	AttrStageCount = "stage_count"
)

The reviewer family. Counts are safe to read at a glance; the identities are the per-entity half and ride the twin only, never a metric label.

View Source
const (
	// AttrRecurrencePatternType is recurrence.pattern.type (e.g. "absoluteMonthly").
	AttrRecurrencePatternType = "recurrence_pattern_type"
	// AttrRecurrenceInterval is recurrence.pattern.interval — the number of
	// pattern units between occurrences (3 absoluteMonthly = quarterly).
	AttrRecurrenceInterval = "recurrence_interval"
	// AttrRecurrenceRangeType is recurrence.range.type (e.g. "noEnd").
	AttrRecurrenceRangeType = "recurrence_range_type"
	// AttrRecurrenceStartDate is recurrence.range.startDate, a bare date.
	AttrRecurrenceStartDate = "recurrence_start_date"
)

The recurrence family — a review's expected CADENCE, read off settings.recurrence. It is the context a reader needs to judge whether a review is overdue, but this collector deliberately does NOT make that judgement itself (see the package doc of internal/collectors/entra/accessreviews on why instances are out of scope).

There is no recurrence_end_date key: the live wire's range.endDate is "9999-12-31" for a never-ending recurrence, a sentinel rather than a date, and AttrRecurrenceRangeType already says "noEnd" without inventing a year-9999 deadline.

View Source
const (
	// AttrInstanceDurationDays is settings.instanceDurationInDays — how long each
	// recurrence stays open for reviewers.
	AttrInstanceDurationDays = "instance_duration_days"
	// AttrMailNotificationsEnabled is settings.mailNotificationsEnabled. With
	// AttrReminderNotificationsEnabled it is the collector's one WARN condition:
	// both false means reviewers are never told the review exists and never
	// reminded.
	AttrMailNotificationsEnabled = "mail_notifications_enabled"
	// AttrReminderNotificationsEnabled is settings.reminderNotificationsEnabled.
	AttrReminderNotificationsEnabled = "reminder_notifications_enabled"
	// AttrAutoApplyDecisionsEnabled is settings.autoApplyDecisionsEnabled —
	// whether decisions are applied without a human pressing apply.
	AttrAutoApplyDecisionsEnabled = "auto_apply_decisions_enabled"
	// AttrDefaultDecisionEnabled is settings.defaultDecisionEnabled — whether
	// non-responses take AttrDefaultDecision.
	AttrDefaultDecisionEnabled = "default_decision_enabled"
	// AttrDefaultDecision is settings.defaultDecision (e.g. "None"). Carried
	// verbatim: its value set has not been established from the wire, so it is
	// neither bucketed nor watched.
	AttrDefaultDecision = "default_decision"
	// AttrJustificationRequiredOnApproval is
	// settings.justificationRequiredOnApproval.
	AttrJustificationRequiredOnApproval = "justification_required_on_approval"
	// AttrRecommendationsEnabled is settings.recommendationsEnabled — whether
	// reviewers are shown Microsoft's approve/deny recommendation.
	AttrRecommendationsEnabled = "recommendations_enabled"
	// AttrApplyActionTypes is settings.applyActions' @odata.type discriminators,
	// prefix-stripped (e.g. "removeAccessApplyAction") — WHAT happens to access
	// when a decision is applied. The second polymorphic member on this record,
	// read the same way as `scope`: by discriminator, never structurally.
	AttrApplyActionTypes = "apply_action_types"
)

The settings family — the governance knobs that decide whether a review can actually change anything. Each is emitted only when the record carried a `settings` object at all, so an absent settings block omits the whole family rather than publishing a row of fabricated falses.

View Source
const (
	// AttrIsActive is the alert's `isActive`: whether Microsoft's last scan
	// found the condition present. It is a metric label — two values, fixed —
	// and the axis an operator alerts on. A row that never states it reads
	// "unknown" rather than "false", because a fabricated inactive alert is a
	// fabricated clean bill of health.
	AttrIsActive = "is_active"
	// AttrIncidentCount is the alert's `incidentCount`: how many entities the
	// finding covers (11 roles without MFA, 5 assignments made outside PIM).
	// The entities themselves are NOT reachable — the `alertIncidents` segment
	// 400s even with the mandatory scope filter (live-measured 2026-07-24) — so
	// this count is the finest granularity that exists on this surface.
	AttrIncidentCount = "incident_count"
	// AttrLastScannedDateTime is the alert's `lastScannedDateTime`: when
	// Microsoft last evaluated the condition. Distinct from
	// AttrLastModifiedDateTime, which is when the finding last CHANGED — and
	// which is the .NET zero date on an alert that has never fired.
	AttrLastScannedDateTime = "last_scanned_date_time"
	// AttrSecurityImpact is the definition's `securityImpact`: Microsoft's prose
	// on what an attacker gains from the condition.
	AttrSecurityImpact = "security_impact"
	// AttrMitigationSteps is the definition's `mitigationSteps`: what to do
	// about the finding now.
	AttrMitigationSteps = "mitigation_steps"
	// AttrHowToPrevent is the definition's `howToPrevent`: what to change so it
	// does not recur. Some values carry HTML anchors and CR-LF-separated bullets
	// verbatim off the wire; they are emitted unaltered.
	AttrHowToPrevent = "how_to_prevent"
	// AttrIsRemediatable is the definition's `isRemediatable`: whether PIM can
	// fix the finding itself. graph2otel never remediates — this says whether a
	// human has a one-click path.
	AttrIsRemediatable = "is_remediatable"
	// AttrIsConfigurable is the definition's `isConfigurable`: whether the
	// alert's thresholds can be changed at all. False on the alerts whose
	// configuration carries no threshold fields.
	AttrIsConfigurable = "is_configurable"
	// AttrAlertEvaluationWindowSeconds is the configuration's `duration`
	// (ISO-8601, "P30D" on both rows that carry it), converted to seconds: the
	// lookback the alert evaluates. It is what makes "2 stale accounts" a
	// statement about 30 days rather than an unqualified number.
	AttrAlertEvaluationWindowSeconds = "alert_evaluation_window_seconds"
	// AttrTimeBetweenActivationsSeconds is the sequential-activation
	// configuration's `timeIntervalBetweenActivations` (ISO-8601, "PT10S"),
	// converted to seconds.
	AttrTimeBetweenActivationsSeconds = "time_between_activations_seconds"
	// AttrSequentialActivationCounterThreshold is that same configuration's
	// `sequentialActivationCounterThreshold` — how many activations inside the
	// interval above trip the alert.
	AttrSequentialActivationCounterThreshold = "sequential_activation_counter_threshold"
	// AttrGlobalAdminCountThreshold is the too-many-global-admins
	// configuration's `globalAdminCountThreshold`.
	AttrGlobalAdminCountThreshold = "global_admin_count_threshold"
	// AttrGlobalAdminPercentageThreshold is that configuration's
	// `percentageOfGlobalAdminsOutOfRolesThreshold` — the second, independent
	// trip wire, expressed as a percentage of all privileged role holders.
	AttrGlobalAdminPercentageThreshold = "global_admin_percentage_threshold"
)
  • Most of the record's keys are NOT here, and that is the point
  • Every key below is LOG-ONLY except two

Attribute keys introduced by entra.pim_alerts (#256), Microsoft's own pre-computed privileged-access findings on `/beta/identityGovernance/roleManagementAlerts`.

Most of the record's keys are NOT here, and that is the point

A PIM alert is three joined records — the alert (state), its definition (meaning) and its configuration (whether it is even switched on) — and the bulk of what they carry already has a constant in this package:

id                   -> AttrAlertId       (attrs_defender.go)
<stripped type>      -> AttrAlertType     (attrs_intune.go)
displayName          -> AttrDisplayName   (attrs_shared.go)
description          -> AttrDescription   (attrs_purview.go)
severityLevel        -> AttrSeverity      (attrs_shared.go)
isEnabled            -> AttrIsEnabled     (attrs_entra.go)
lastModifiedDateTime -> AttrLastModifiedDateTime (attrs_m365.go)

Those are REUSED rather than re-coined, so a `severity` filter means the same thing across entra.pim_alerts, entra.security_alerts and the Defender signals. The registry's no-duplicate-values gate enforces it from the other direction: a second constant carrying "severity" is a build failure.

Every key below is LOG-ONLY except two

AttrIsActive is a bounded two-value flag and rides the alert gauge; everything else here is either free prose (the remediation text) or a per-alert number, and belongs on the twin (#112/#114). AttrIncidentCount is the sharpest case: it is the COUNT of flagged entities, so it is a metric VALUE (entra.pim.alert.incidents) and a twin attribute — never a label.

View Source
const (
	AttrAccountSetupDurationSeconds      = "account_setup_duration_seconds"
	AttrAccountSetupStatus               = "account_setup_status"
	AttrActivityOperationType            = "activity_operation_type"
	AttrActivityResult                   = "activity_result"
	AttrActivityType                     = "activity_type"
	AttrActorApplicationDisplayName      = "actor_application_display_name"
	AttrActorApplicationId               = "actor_application_id"
	AttrActorIpAddress                   = "actor_ip_address"
	AttrActorType                        = "actor_type"
	AttrActorUserId                      = "actor_user_id"
	AttrActorUserPrincipalName           = "actor_user_principal_name"
	AttrAlertDisplayName                 = "alert_display_name"
	AttrAlertType                        = "alert_type"
	AttrAnomalySeverity                  = "anomaly_severity"
	AttrAntiMalwareVersion               = "anti_malware_version"
	AttrAppIdentifier                    = "app_identifier"
	AttrAppName                          = "app_name"
	AttrAppReliabilityScore              = "app_reliability_score"
	AttrAppType                          = "app_type"
	AttrAssigned                         = "assigned"
	AttrAssignmentStatusCode             = "assignment_status_code"
	AttrAttestationStatus                = "attestation_status"
	AttrAttestationStatusDetail          = "attestation_status_detail"
	AttrBaselineName                     = "baseline_name"
	AttrBatteryHealthScore               = "battery_health_score"
	AttrBucket                           = "bucket"
	AttrCertProfileName                  = "cert_profile_name"
	AttrCertificateProfileName           = "certificate_profile_name"
	AttrCertificateStatus                = "certificate_status"
	AttrComplianceGracePeriodExpiration  = "compliance_grace_period_expiration"
	AttrComplianceState                  = "compliance_state"
	AttrComplianceStatus                 = "compliance_status"
	AttrCompliantDeviceCount             = "compliant_device_count"
	AttrComponentName                    = "component_name"
	AttrConfigName                       = "config_name"
	AttrConfigType                       = "config_type"
	AttrConnectorType                    = "connector_type"
	AttrConflictDeviceCount              = "conflict_device_count"
	AttrDeploymentDurationSeconds        = "deployment_duration_seconds"
	AttrDeploymentState                  = "deployment_state"
	AttrDeviceDisplayName                = "device_display_name"
	AttrDeviceDnsDomain                  = "device_dns_domain"
	AttrDeviceHostName                   = "device_host_name"
	AttrDeviceId                         = "device_id"
	AttrDeviceName                       = "device_name"
	AttrDeviceNetBiosName                = "device_net_bios_name"
	AttrDevicePlatform                   = "device_platform"
	AttrDeviceSerialNumber               = "device_serial_number"
	AttrDeviceSetupDurationSeconds       = "device_setup_duration_seconds"
	AttrDeviceSetupStatus                = "device_setup_status"
	AttrDeviceState                      = "device_state"
	AttrDeviceStateCode                  = "device_state_code"
	AttrDeviceTag                        = "device_tag"
	AttrDeviceType                       = "device_type"
	AttrEnabled                          = "enabled"
	AttrEndpointAnalyticsScore           = "endpoint_analytics_score"
	AttrEngineVersion                    = "engine_version"
	AttrEnhancedKeyUsage                 = "enhanced_key_usage"
	AttrEnrollmentFailureDetails         = "enrollment_failure_details"
	AttrEnrollmentState                  = "enrollment_state"
	AttrEnrollmentType                   = "enrollment_type"
	AttrErrorCode                        = "error_code"
	AttrErrorDeviceCount                 = "error_device_count"
	AttrExpediteReleaseDate              = "expedite_release_date"
	AttrFailedDeviceCount                = "failed_device_count"
	AttrFailureCategory                  = "failure_category"
	AttrFailureReason                    = "failure_reason"
	AttrFeatureUpdateVersion             = "feature_update_version"
	AttrFileDescription                  = "file_description"
	AttrFirewallStatus                   = "firewall_status"
	AttrFlaggedReason                    = "flagged_reason"
	AttrFlaggedReasons                   = "flagged_reasons"
	AttrFullScanOverdue                  = "full_scan_overdue"
	AttrFullScanRequired                 = "full_scan_required"
	AttrGroupTag                         = "group_tag"
	AttrHash                             = "hash"
	AttrHealthState                      = "health_state"
	AttrIngestionType                    = "ingestion_type"
	AttrInstallState                     = "install_state"
	AttrInstalledDeviceCount             = "installed_device_count"
	AttrIntendedPurpose                  = "intended_purpose"
	AttrIntentName                       = "intent_name"
	AttrIntuneAccountId                  = "intune_account_id"
	AttrIntuneUserId                     = "intune_user_id"
	AttrIsAdminSelected                  = "is_admin_selected"
	AttrIsBuiltIn                        = "is_built_in"
	AttrIsEncrypted                      = "is_encrypted"
	AttrIsVirtualMachine                 = "is_virtual_machine"
	AttrIssuanceDateTime                 = "issuance_date_time"
	AttrIssuanceState                    = "issuance_state"
	AttrIssuer                           = "issuer"
	AttrIssuerName                       = "issuer_name"
	AttrKeyLength                        = "key_length"
	AttrKeyName                          = "key_name"
	AttrLastCheckin                      = "last_checkin"
	AttrLastFullScanDateTime             = "last_full_scan_date_time"
	AttrLastFullScanSignatureVersion     = "last_full_scan_signature_version"
	AttrLastIssuanceStateChangedDateTime = "last_issuance_state_changed_date_time"
	AttrLastQuickScanDateTime            = "last_quick_scan_date_time"
	AttrLastQuickScanSignatureVersion    = "last_quick_scan_signature_version"
	AttrLastReportedDateTime             = "last_reported_date_time"
	AttrLastSyncDateTime                 = "last_sync_date_time"
	AttrMalwareProtectionEnabled         = "malware_protection_enabled"
	AttrManufacturer                     = "manufacturer"
	AttrMigrating                        = "migrating"
	AttrModel                            = "model"
	AttrMonthElevationCount              = "month_elevation_count"
	AttrNetworkInspectionSystemEnabled   = "network_inspection_system_enabled"
	AttrNotApplicableDeviceCount         = "not_applicable_device_count"
	AttrNotInstalledDeviceCount          = "not_installed_device_count"
	AttrOdataType                        = "odata_type"
	AttrOperationalLogCategory           = "operational_log_category"
	AttrOs                               = "os"
	AttrOsVersion                        = "os_version"
	AttrOwnership                        = "ownership"
	AttrPartnerReportedThreatState       = "partner_reported_threat_state"
	AttrPendingInstallDeviceCount        = "pending_install_device_count"
	AttrPhase                            = "phase"
	AttrPlatform                         = "platform"
	AttrPlatformCode                     = "platform_code"
	AttrPolicyId                         = "policy_id"
	AttrPolicyInstallStatus              = "policy_install_status"
	AttrPolicyName                       = "policy_name"
	AttrPolicyPlatform                   = "policy_platform"
	AttrPolicyStatus                     = "policy_status"
	AttrPolicyType                       = "policy_type"
	AttrPreprovisioningAllowed           = "preprovisioning_allowed"
	AttrProductStatus                    = "product_status"
	AttrProductStatusCode                = "product_status_code"
	AttrProductStatusRaw                 = "product_status_raw"
	AttrProfileName                      = "profile_name"
	AttrProviderName                     = "provider_name"
	AttrPublisher                        = "publisher"
	AttrPublishingState                  = "publishing_state"
	AttrQuickScanOverdue                 = "quick_scan_overdue"
	AttrReadiness                        = "readiness"
	AttrRealTimeProtectionEnabled        = "real_time_protection_enabled"
	AttrRebootRequired                   = "reboot_required"
	AttrReportName                       = "report_name"
	AttrReportStatus                     = "report_status"
	AttrResourceDisplayNames             = "resource_display_names"
	AttrResourceIds                      = "resource_ids"
	AttrResourceType                     = "resource_type"
	AttrResourceTypes                    = "resource_types"
	AttrRestartCategory                  = "restart_category"
	AttrRevokeStatus                     = "revoke_status"
	AttrRingName                         = "ring_name"
	AttrRunState                         = "run_state"
	AttrScaleUnit                        = "scale_unit"
	AttrScenarioName                     = "scenario_name"
	AttrScriptName                       = "script_name"
	AttrSerialNumber                     = "serial_number"
	AttrSetting                          = "setting"
	AttrSettingDeviceStatus              = "setting_device_status"
	AttrSettingDisplayName               = "setting_display_name"
	AttrSettingId                        = "setting_id"
	AttrSettingName                      = "setting_name"
	AttrSettingStatus                    = "setting_status"
	AttrSettingStatusCode                = "setting_status_code"
	AttrSignal                           = "signal"
	AttrSignatureUpdateOverdue           = "signature_update_overdue"
	AttrSignatureVersion                 = "signature_version"
	AttrStalenessBucket                  = "staleness_bucket"
	AttrStartupPerformanceScore          = "startup_performance_score"
	AttrStateBucket                      = "state_bucket"
	AttrSubjectAlternativeNameFormat     = "subject_alternative_name_format"
	AttrSubjectName                      = "subject_name"
	AttrSubjectNameFormat                = "subject_name_format"
	AttrTamperProtectionEnabled          = "tamper_protection_enabled"
	AttrTarget                           = "target"
	AttrTechnology                       = "technology"
	AttrTemplateFamily                   = "template_family"
	AttrThumbprint                       = "thumbprint"
	AttrTokenName                        = "token_name"
	AttrTpmManufacturer                  = "tpm_manufacturer"
	AttrTpmVersion                       = "tpm_version"
	AttrUnifiedPolicyType                = "unified_policy_type"
	AttrUpdateDeploymentState            = "update_deployment_state"
	AttrUpdateType                       = "update_type"
	AttrUpn                              = "upn"
	AttrUpnSuffix                        = "upn_suffix"
	AttrUserName                         = "user_name"
	AttrValidFrom                        = "valid_from"
	AttrValidTo                          = "valid_to"
	AttrWifiMacAddress                   = "wifi_mac_address"
	AttrWorkFromAnywhereScore            = "work_from_anywhere_score"
)

Attribute keys used only by intune.* collectors.

View Source
const (
	AttrAikKey                       = "aik_key"
	AttrAttestationError             = "attestation_error"
	AttrBitlockerStatus              = "bitlocker_status"
	AttrBootDebuggingStatus          = "boot_debugging_status"
	AttrCodeIntegrityStatus          = "code_integrity_status"
	AttrCurrentProvisioningPhase     = "current_provisioning_phase"
	AttrDepPolicy                    = "dep_policy"
	AttrDeploymentStatus             = "deployment_status"
	AttrElamDriverLoadedStatus       = "elam_driver_loaded_status"
	AttrElevationCount               = "elevation_count"
	AttrElevationType                = "elevation_type"
	AttrEnrollmentTime               = "enrollment_time"
	AttrFileHash                     = "file_hash"
	AttrFileVersion                  = "file_version"
	AttrFirmwareProtectionStatus     = "firmware_protection_status"
	AttrHealthCertIssuedDate         = "health_cert_issued_date"
	AttrInternalName                 = "internal_name"
	AttrIsBackgroundProcess          = "is_background_process"
	AttrMemoryAccessProtectionStatus = "memory_access_protection_status"
	AttrMemoryIntegrityProtection    = "memory_integrity_protection_status"
	AttrOsKernelDebuggingStatus      = "os_kernel_debugging_status"
	AttrPosture                      = "posture"
	AttrResultCode                   = "result_code"
	AttrSafeModeStatus               = "safe_mode_status"
	AttrSecureBootStatus             = "secure_boot_status"
	AttrSecuredCorePcStatus          = "secured_core_pc_status"
	AttrSystemManagementMode         = "system_management_mode"
	AttrVsmStatus                    = "vsm_status"
	AttrWinpeStatus                  = "winpe_status"
)

Attribute keys for the Intune reports-export collectors added in the #192–#195 reporting build-out: device boot-security (WindowsDeviceHealthAttestationReport, #195), Autopilot device-prep deployment (AutopilotV2DeploymentStatus, #193), and Endpoint Privilege Management elevations (EpmAggregationReportByApplication, #193). AttrCompanyName / AttrFileName are reused from attrs_defender.go (one key, one const — enforced by the registry gate). Every value here is live-captured from the export CSV header, not a doc placeholder.

View Source
const (
	AttrElevationId       = "elevation_id"
	AttrFilePath          = "file_path"
	AttrIsSystemInitiated = "is_system_initiated"
	AttrJustification     = "justification"
	AttrParentProcessName = "parent_process_name"
	AttrProcessType       = "process_type"
	AttrProductName       = "product_name"
	AttrRuleId            = "rule_id"
)

Attribute keys for the intune.epm_elevation_events per-elevation SIEM stream (EpmElevationReportElevationEvent, #205) — the per-event detail behind the EpmAggregationReportByApplication rollup above. Every value is live-captured from the export CSV header (probed as graph2otel-poller 2026-07-20), not a doc placeholder. All are twin-only (per-entity) attributes; the metric carries only the bounded elevation_type/result pair (#112).

View Source
const (
	AttrDetectionOutput        = "detection_output"
	AttrDetectionScriptError   = "detection_script_error"
	AttrDetectionState         = "detection_state"
	AttrLastStateUpdate        = "last_state_update"
	AttrRemediationId          = "remediation_id"
	AttrRemediationName        = "remediation_name"
	AttrRemediationScriptError = "remediation_script_error"
	AttrRemediationState       = "remediation_state"
)

Attribute keys for intune.remediation_run_states (#207) — per-device proactive remediation (deviceHealthScripts) run state, read live from beta /deviceManagement/deviceHealthScripts/{id}/deviceRunStates. detection_state and remediation_state are the two bounded EPM/remediation enums the gauge is keyed by; the rest (the detection script's output message, script errors, timing) ride the log twin only.

View Source
const (
	AttrAdvancedBitlockerStates      = "advanced_bitlocker_states"
	AttrEncryptionPolicySettingState = "encryption_policy_setting_state"
	AttrEncryptionReadinessState     = "encryption_readiness_state"
	AttrEncryptionState              = "encryption_state"
	AttrFileVaultStates              = "file_vault_states"
)

Attribute keys for intune.device_encryption (#199) — per-device disk-encryption posture from beta /deviceManagement/managedDeviceEncryptionStates (v1.0 has no such segment). encryption_state / encryption_readiness_state / encryption_policy_setting_state are the three bounded wire enums the gauges are keyed by (alongside the existing device_type); advanced_bitlocker_states is a comma-joined flag list whose COMBINATIONS are unbounded and file_vault_states is its Apple counterpart — both are twin-only, never a metric label (#112/#114). Every value is live-captured from the beta wire (probed as graph2otel-poller 2026-07-21), not a doc placeholder.

View Source
const (
	AttrCloudIdentityScore            = "cloud_identity_score"
	AttrCloudManagementScore          = "cloud_management_score"
	AttrCloudProvisioningScore        = "cloud_provisioning_score"
	AttrOsCheckFailed                 = "os_check_failed"
	AttrProcessor64BitCheckFailed     = "processor_64bit_check_failed"
	AttrProcessorCoreCountCheckFailed = "processor_core_count_check_failed"
	AttrProcessorFamilyCheckFailed    = "processor_family_check_failed"
	AttrProcessorSpeedCheckFailed     = "processor_speed_check_failed"
	AttrRamCheckFailed                = "ram_check_failed"
	AttrSecureBootCheckFailed         = "secure_boot_check_failed"
	AttrStorageCheckFailed            = "storage_check_failed"
	AttrTpmCheckFailed                = "tpm_check_failed"
	AttrUpgradeEligibility            = "upgrade_eligibility"
	AttrWindowsScore                  = "windows_score"
)

Attribute keys for the Endpoint Analytics Work-From-Anywhere per-device Windows 11 upgrade-readiness signal (#194) — the metricDevices navigation under userExperienceAnalyticsWorkFromAnywhereMetrics. Values live-captured from the beta wire 2026-07-19 on m7kni.

View Source
const (
	// Bare snake_case, not a dotted key: every domain attribute in this repo is
	// bare, and this one is a METRIC label, so a dot would survive to the wire
	// but reach PromQL normalized to an underscore (#82) — the golden, the docs
	// and the query surface would then disagree about the label's own name.
	AttrElevationGovernance = "elevation_governance"
	AttrManagedCount        = "managed_count"
	AttrTotalCount          = "total_count"
	AttrUnmanagedCount      = "unmanaged_count"
)

Attribute keys for the two Endpoint Privilege Management attribution cuts added in #201 — intune.epm_elevations_by_user (EpmAggregationReportByUser) and intune.epm_elevations_by_publisher (EpmAggregationReportByPublisher). Both are siblings of intune.epm_elevations (EpmAggregationReportByApplication), so elevation_type / elevation_count / company_name are reused above rather than redeclared. Every value here is live-captured from the export CSV header (probed as graph2otel-poller 2026-07-21 on m7kni), not a doc placeholder.

AttrElevationGovernance is the by-user gauge's ONLY label: the report gives a managed/unmanaged split per user, and the two counts are summed into exactly two bounded series. The Upn column rides the log twin only — it identifies a user, so it can never be a metric label (#112), and it is emitted VERBATIM: one live row carried the down-level logon name `AzureAD\RobKnight` rather than a real UPN, so nothing here parses or validates it.

View Source
const (
	ElevationGovernanceManaged   = "managed"
	ElevationGovernanceUnmanaged = "unmanaged"
)

Elevation-governance values for AttrElevationGovernance: the two bounded series intune.epm_elevations_by_user's gauge emits, always both, even at zero.

View Source
const (
	// Battery detail (userExperienceAnalyticsBatteryHealthDevicePerformance).
	// These are the fields that EXPLAIN a battery health score: a bare score of
	// 63 is not actionable, "63, 179 days old, 100% max capacity, 80 minutes
	// estimated runtime" is.
	AttrBatteryAgeDays          = "battery_age_days"
	AttrBatteryCount            = "battery_count"
	AttrBatteryIds              = "battery_ids"
	AttrEstimatedRuntimeMinutes = "estimated_runtime_minutes"
	AttrFullBatteryDrainCount   = "full_battery_drain_count"
	AttrMaxCapacityPercentage   = "max_capacity_percentage"

	// Boot-event detail (userExperienceAnalyticsDeviceStartupHistory). Unlike the
	// state twins, a startup history row is an EVENT with its own startTime, so
	// its twin is stamped with that time rather than poll time.
	// restart_stop_code / restart_fault_bucket are the Windows crash-bucket
	// identifiers — the only genuinely diagnostic fields in the set, and the
	// reason the per-boot twin was worth overriding the exception for.
	AttrCoreBootTimeMs          = "core_boot_time_ms"
	AttrCoreLoginTimeMs         = "core_login_time_ms"
	AttrFeatureUpdateBootTimeMs = "feature_update_boot_time_ms"
	AttrGroupPolicyBootTimeMs   = "group_policy_boot_time_ms"
	AttrGroupPolicyLoginTimeMs  = "group_policy_login_time_ms"
	AttrIsFeatureUpdate         = "is_feature_update"
	AttrIsFirstLogin            = "is_first_login"
	AttrResponsiveDesktopTimeMs = "responsive_desktop_time_ms"
	AttrRestartFaultBucket      = "restart_fault_bucket"
	AttrRestartStopCode         = "restart_stop_code"
	AttrTotalBootTimeMs         = "total_boot_time_ms"
	AttrTotalLoginTimeMs        = "total_login_time_ms"

	// Startup-process detail (userExperienceAnalyticsDeviceStartupProcesses).
	// process_name is per-process and combines with the device, so the pair is
	// unbounded — twin only. product_name and publisher are reused from above.
	AttrProcessName     = "process_name"
	AttrStartupImpactMs = "startup_impact_ms"

	// Per-device app-health detail (userExperienceAnalyticsAppHealthDevicePerformance),
	// the device-level sibling of the application-level segment that is empty on
	// m7kni under the 5-device Endpoint Analytics floor.
	AttrAppCrashCount            = "app_crash_count"
	AttrAppHangCount             = "app_hang_count"
	AttrCrashedAppCount          = "crashed_app_count"
	AttrDeviceAppHealthScore     = "device_app_health_score"
	AttrMeanTimeToFailureMinutes = "mean_time_to_failure_minutes"

	// AttrMeanResourceSpikeTimeScore is the sixth Endpoint Analytics score
	// category. It is on the wire of BOTH userExperienceAnalyticsDeviceScores and
	// userExperienceAnalyticsModelScores and was simply never mapped — the
	// original deviceScore struct predates it (live-measured 2026-07-24, #194:
	// 100.0 on wintest, 64.33/64.81/91.73/92.62 on the four load-generating VMs,
	// and the -1 sentinel on the rest).
	AttrMeanResourceSpikeTimeScore = "mean_resource_spike_time_score"

	// Per-APPLICATION app-health detail
	// (userExperienceAnalyticsAppHealthApplicationPerformance). Distinct from the
	// per-device block above: one row per application across the fleet, so the
	// app name is the entity. These are twin-only — the application set is
	// unbounded, which is exactly why intune.uxa.app_crash_count keeps a fixed
	// allow-list as its metric boundary while every row still gets a log record
	// (#114; the drop was the bug, the allow-list is not).
	AttrActiveDeviceCount = "active_device_count"
	AttrAppHealthScore    = "app_health_score"
	AttrAppUsageDuration  = "app_usage_duration"

	// Per-device resource detail (userExperienceAnalyticsResourcePerformance).
	// These names WERE EDM-derived while the segment was empty on m7kni; that
	// caveat is withdrawn — the segment returned a row on 2026-07-23 and every
	// mapped name matched the wire, so the whole block is now
	// [live-measured 2026-07-23, #194]. Values are still emitted only when
	// present, which is why a wrong name would have yielded an absent attribute
	// rather than a wrong one.
	//
	// The two *_threshold keys are the TENANT'S OWN policy values, not the
	// device's readings: they say what CPU/RAM spike percentage Endpoint
	// Analytics considers bad here (15% / 30% on m7kni), which is what turns a
	// bare "cpu_spike_time_percentage=12" into a judgement.
	AttrAverageSpikeTimeScore           = "average_spike_time_score"
	AttrCpuClockSpeedMhz                = "cpu_clock_speed_mhz"
	AttrCpuDisplayName                  = "cpu_display_name"
	AttrCpuSpikeTimePercentage          = "cpu_spike_time_percentage"
	AttrCpuSpikeTimePercentageThreshold = "cpu_spike_time_percentage_threshold"
	AttrCpuSpikeTimeScore               = "cpu_spike_time_score"
	AttrDiskType                        = "disk_type"
	AttrMachineType                     = "machine_type"
	AttrProcessorCoreCount              = "processor_core_count"
	AttrRamSpikeTimePercentage          = "ram_spike_time_percentage"
	AttrRamSpikeTimePercentageThreshold = "ram_spike_time_percentage_threshold"
	AttrRamSpikeTimeScore               = "ram_spike_time_score"
	AttrResourcePerformanceScore        = "resource_performance_score"
	AttrTotalRamMb                      = "total_ram_mb"
)

Attribute keys for the intune.endpoint_analytics per-entity log twins added in #225, when the maintainer overrode the #114 no-twin exception this collector had carried since the original audit. Every key here is TWIN-ONLY: the bounded gauges and histograms keep their existing health_state / restart_category / category labels, and none of the per-device values below may become a metric label (#112) — device identity, boot timings and battery serial detail are all either unbounded or grow with fleet size.

Values are live-captured from the beta/v1.0 wire (probed as graph2otel-poller against m7kni, 2026-07-21) except where noted on AttrCpuDisplayName's block, which is EDM-derived because the segment is empty on that tenant.

View Source
const (
	AttrBatteryChargeCycles                 = "battery_charge_cycles"
	AttrBatteryHealthPercentage             = "battery_health_percentage"
	AttrBatteryLevelPercentage              = "battery_level_percentage"
	AttrCellularTechnology                  = "cellular_technology"
	AttrCredentialGuardState                = "credential_guard_state"
	AttrDeviceGuardHardwareRequirementState = "device_guard_hardware_requirement_state"
	AttrDeviceLicensingStatus               = "device_licensing_status"
	AttrEsimIdentifier                      = "esim_identifier"
	AttrFreeStorageBytes                    = "free_storage_bytes"
	AttrImei                                = "imei"
	AttrIsSharedDevice                      = "is_shared_device"
	AttrIsSupervised                        = "is_supervised"
	AttrOperatingSystemEdition              = "operating_system_edition"
	AttrOperatingSystemLanguage             = "operating_system_language"
	AttrOperatingSystemProductType          = "operating_system_product_type"
	AttrPhoneNumber                         = "phone_number"
	AttrStorageState                        = "storage_state"
	AttrSubscriberCarrier                   = "subscriber_carrier"
	AttrSystemManagementBiosVersion         = "system_management_bios_version"
	// AttrTpmSpecificationVersion is DISTINCT from AttrTpmVersion. The wire
	// carries both, and they are not the same thing: the specification version is
	// a comma-joined triple ("2.0, 0, 1.64") that is constant across a fleet's TPM
	// revisions — bounded, so it is the gauge's label — while tpmVersion is the
	// chip's own firmware version ("8217.4131.22.13878"), per-entity and twin-only.
	AttrTpmSpecificationVersion = "tpm_specification_version"
	AttrTotalStorageBytes       = "total_storage_bytes"
	AttrVbsState                = "vbs_state"
	AttrWiredIpv4Addresses      = "wired_ipv4_addresses"
)

Attribute keys for intune.hardware_inventory (#199) — the per-device `hardwareInformation` complex type, which exists ONLY on the beta managedDevice and only materializes on a SINGLE-ENTITY GET (the list form returns a stub). Every value here is live-captured from the beta wire via $batch (probed as graph2otel-poller 2026-07-21 on m7kni), not a doc placeholder.

Only four of these are metric labels — AttrTpmSpecificationVersion, AttrVbsState, AttrCredentialGuardState and AttrStorageState — and each is a bounded wire enum or a per-fleet-constant version triple. Everything else is per-entity (storage bytes, TPM instance version, wired IPs, cellular identity) and rides the intune.device_hardware log twin only (#112/#114).

device_id / device_name / operating_system / manufacturer / product_name / tpm_version / tpm_manufacturer are reused from above rather than redeclared — one key, one constant.

View Source
const (
	StorageStateTotal = "total"
	StorageStateFree  = "free"
)

Storage-state values for AttrStorageState: the two series intune.hardware_inventory.storage_bytes emits per operating system.

View Source
const (
	AttrBindStatus            = "bind_status"
	AttrEnrollmentTarget      = "enrollment_target"
	AttrLastAppSyncStatus     = "last_app_sync_status"
	AttrLastManualSyncTrigger = "last_manual_sync_trigger_date_time"
	AttrSyncStatus            = "sync_status"
)

Attribute keys for the two #248 Intune device-management health folds, both reading a BETA singleton onto an existing collector's fetch cycle:

  • intune/connectors — the Managed Google Play (Android managed store) connector, added as a fourth connector_type on the existing intune.connector.state / heartbeat_age_seconds metrics plus one intune.connector log twin. bind_status / last_app_sync_status / enrollment_target are twin-only; owner UPN reuses AttrOwnerPrincipalName, connector_type / state / last_sync_date_time are reused from above.
  • intune/autopilot — the windowsAutopilotSettings device-registration sync, added as intune.autopilot.sync_age_seconds / sync_status plus one intune.autopilot.sync log twin. sync_status is the metric label; last_manual_sync_trigger_date_time is twin-only; id / last_sync_date_time are reused from above.

Every value here is live-captured from the beta wire (probed as graph2otel-poller against m7kni 2026-07-23), not a doc placeholder.

View Source
const (
	// AttrCertificationAuthorityId is the CA's own id. It is the join key from a
	// leaf-certificate record back to its issuing CA, and it is LOG-ONLY: `id`
	// is on the #112 per-entity deny list for metric labels, so the bounded CA
	// gauges are keyed by display name instead.
	AttrCertificationAuthorityId = "certification_authority_id"
	// AttrCertificationAuthorityType is the wire's `cloudCertificationAuthorityType`
	// — `rootCertificationAuthority` or `issuingCertificationAuthority`. A
	// bounded enum, and a gauge dimension: an expiring ISSUING CA stops new
	// issuance, an expiring ROOT invalidates the whole chain.
	AttrCertificationAuthorityType = "certification_authority_type"
	// AttrCertificationAuthorityStatus is the wire's `certificationAuthorityStatus`
	// (`active`, …) — a bounded enum and a gauge dimension.
	AttrCertificationAuthorityStatus = "certification_authority_status"
	// AttrCertificationAuthorityIssuerId is the parent CA's id on an issuing CA;
	// empty string on a root, where the attribute is omitted.
	AttrCertificationAuthorityIssuerId = "certification_authority_issuer_id"
	// AttrCertificationAuthorityIssuerUri is the AIA URI the parent CA
	// certificate is fetched from. Empty on a root.
	AttrCertificationAuthorityIssuerUri = "certification_authority_issuer_uri"
	// AttrCertificateRevocationListUrl is the CA's published CRL endpoint.
	AttrCertificateRevocationListUrl = "certificate_revocation_list_url"
	// AttrCrlDistributionPointUrl is the leaf certificate's CDP — the CRL a
	// relying party checks that leaf against.
	AttrCrlDistributionPointUrl = "crl_distribution_point_url"
	// AttrOcspResponderUri is the OCSP responder URI. Empty string on every live
	// row, so the attribute is omitted in practice.
	AttrOcspResponderUri = "ocsp_responder_uri"
	// AttrScepServerUrl is the issuing CA's SCEP enrollment endpoint — the thing
	// devices actually talk to. Null on a root CA (roots do not issue to
	// devices), which is what distinguishes the two in practice. The live value
	// contains an unexpanded `{{CloudPKIFQDN}}` template token and is emitted
	// verbatim rather than "corrected" (#142).
	AttrScepServerUrl = "scep_server_url"
	// AttrCertificateKeySize is the wire's `certificateKeySize` enum (`rsa4096`).
	AttrCertificateKeySize = "certificate_key_size"
	// AttrHashingAlgorithm is the wire's `cloudCertificationAuthorityHashingAlgorithm`
	// (`sha512`).
	AttrHashingAlgorithm = "hashing_algorithm"
	// AttrKeyPlatform is where the CA's private key lives — `hardwareSecurityModule`
	// on the live tenant. A software-backed key is a materially weaker CA.
	AttrKeyPlatform = "key_platform"
	// AttrGeographicRegion is the region the CA is hosted in (`Europe`).
	AttrGeographicRegion = "geographic_region"
	// AttrCommonName is the CA subject's CN component, as the wire reports it.
	AttrCommonName = "common_name"
	// AttrRootCertificateCommonName is the CN of the root at the top of this
	// CA's chain. Null on a root itself.
	AttrRootCertificateCommonName = "root_certificate_common_name"
	// AttrOrganizationName is the CA subject's O component.
	AttrOrganizationName = "organization_name"
	// AttrOrganizationUnit is the CA subject's OU component.
	AttrOrganizationUnit = "organization_unit"
	// AttrLocalityName is the CA subject's L component.
	AttrLocalityName = "locality_name"
	// AttrStateName is the CA subject's ST component.
	AttrStateName = "state_name"
	// AttrCountryName is the CA subject's C component.
	AttrCountryName = "country_name"
	// AttrValidityPeriodYears is the wire's `validityPeriodInYears` — the CA's
	// configured lifetime (25 for the live root, 10 for its issuer).
	AttrValidityPeriodYears = "validity_period_years"
	// AttrVersionNumber is the CA's current version number. A CA is re-versioned
	// when it is renewed, so a version bump is a renewal event.
	AttrVersionNumber = "version_number"

	// AttrExpirySource records WHERE the emitted validity window came from:
	// `certificate` when it was parsed out of the inline DER (the normal path,
	// and the authoritative one — it is the certificate devices actually
	// validate), or `declared` when the certificate could not be parsed and the
	// wire's own `validityEndDateTime` was used instead. Without this key a
	// degraded reading is indistinguishable from a measured one.
	AttrExpirySource = "expiry_source"
	// AttrDeclaredValidTo is the wire's `validityEndDateTime`, and it is emitted
	// ONLY when it disagrees with the certificate's own NotAfter. They agree on
	// every live row, so the attribute's PRESENCE is the anomaly: Graph and the
	// certificate it handed over do not say the same thing about when fleet-wide
	// authentication stops working.
	AttrDeclaredValidTo = "declared_valid_to"

	// AttrLeafCertificateId is a leaf certificate's own id.
	AttrLeafCertificateId = "leaf_certificate_id"
	// AttrRevocationDateTime is when a leaf certificate was revoked. Null on
	// active and expired certificates, where the attribute is omitted — so its
	// presence distinguishes a revoked certificate from a lapsed one.
	AttrRevocationDateTime = "revocation_date_time"
	// AttrKeyUsages is the leaf certificate's X.509 key-usage list
	// (`KeyEncipherment`, `DigitalSignature`).
	//
	// Wire trap: Graph types this as `Collection(Edm.String)` and returns a
	// ONE-element collection whose single element is a JSON-encoded array
	// (`["[\"KeyEncipherment\",\"DigitalSignature\"]"]`). The mapper unwraps
	// that; emitting it raw would put a quoted JSON blob in a list attribute.
	AttrKeyUsages = "key_usages"
	// AttrExtendedKeyUsages is the extended key usage list. On a LEAF it is the
	// same doubly-encoded shape as AttrKeyUsages and carries raw OIDs
	// (`1.3.6.1.5.5.7.3.2`). On a CA the same-named wire field is a collection of
	// `{name, objectIdentifier}` OBJECTS instead, and the human `name` is what is
	// emitted — two different wire shapes behind one attribute name, both
	// normalized to a list of strings here.
	AttrExtendedKeyUsages = "extended_key_usages"
)
  • What is reused rather than re-coined

Attribute keys introduced by intune.cloud_pki (#258) — Intune Cloud PKI, the private CA hierarchy whose certificates devices use for Wi-Fi, VPN and 802.1X authentication. When an issuing CA expires, every device depending on it loses authentication at once, with no gradual degradation to notice first.

What is reused rather than re-coined

The certificate-shaped half of both records already has a key here, so a Cloud PKI certificate answers the same filters as an Intune SCEP/PKCS certificate:

subjectName          -> AttrSubjectName       ("subject_name")
issuerName           -> AttrIssuerName        ("issuer_name")
thumbprint           -> AttrThumbprint        ("thumbprint")
serialNumber         -> AttrSerialNumber      ("serial_number")
certificateStatus    -> AttrCertificateStatus ("certificate_status")
NotBefore / NotAfter -> AttrValidFrom / AttrValidTo
expiry window        -> AttrExpiryBucket      ("expiry_bucket")
devicePlatform       -> AttrDevicePlatform    ("device_platform")

AttrValidFrom/AttrValidTo carry the values PARSED OUT OF THE CERTIFICATE, not the wire's `validityStartDateTime`/`validityEndDateTime` — see AttrExpirySource and AttrDeclaredValidTo below.

The certificate blob itself (`certificateDownloadUrl`, a `data:application/x-x509-ca-cert;base64,…` URI) and `certificateSigningRequest` are never emitted: the blob is public, not secret, but it is kilobytes of base64 that no log query can use, and its only useful content — the validity window, subject and issuer — is decoded into the fields above.

View Source
const (
	// AttrRoleDefinitionType is the wire's `@odata.type` discriminator with the
	// `#microsoft.graph.` prefix stripped — `deviceAndAppManagementRoleDefinition`
	// on every live row. It is READ rather than assumed because a different
	// subtype may carry a different field set. Bounded (a handful of subtypes),
	// so it is a legitimate gauge dimension.
	AttrRoleDefinitionType = "role_definition_type"
	// AttrIsBuiltInRoleDefinition is the wire's `isBuiltInRoleDefinition`, which
	// is present ALONGSIDE `isBuiltIn` on the same record and agrees with it on
	// every live row. Both are emitted, and both are gauge dimensions, precisely
	// so a future disagreement is visible as a new series rather than silently
	// resolved by a mapper that assumed one was a rename of the other (#142).
	AttrIsBuiltInRoleDefinition = "is_built_in_role_definition"
	// AttrNotAllowedActions is the union of
	// `permissions[].resourceActions[].notAllowedResourceActions` — the explicit
	// deny half of a role's permission set. Empty on every live row; the
	// attribute is omitted rather than emitted blank.
	AttrNotAllowedActions = "not_allowed_actions"
	// AttrRoleScopeTagIds is the role's `roleScopeTagIds` collection: the scope
	// tags that bound which objects the role can see.
	AttrRoleScopeTagIds = "role_scope_tag_ids"
	// AttrAssignmentCount is how many role assignments reference this role
	// definition. A custom role with zero assignments grants nothing; the same
	// role with assignments is the thing worth looking at.
	AttrAssignmentCount = "assignment_count"

	// AttrRoleAssignmentId is the assignment's own id — distinct from
	// AttrRoleId, which is the id of the ROLE it grants.
	AttrRoleAssignmentId = "role_assignment_id"
	// AttrScopeType is the assignment's `scopeType` — a bounded wire enum whose
	// widest value, `allDevicesAndLicensedUsers`, means tenant-wide device
	// management. It is a gauge dimension and drives severity.
	AttrScopeType = "scope_type"
	// AttrScopeMembers is the assignment's `scopeMembers` collection: the group
	// object ids that bound WHICH devices/users the assignment covers. Empty on
	// an `allDevicesAndLicensedUsers` assignment, which is what makes that scope
	// type unbounded.
	AttrScopeMembers = "scope_members"
	// AttrResourceScopes is the assignment's `resourceScopes` collection.
	AttrResourceScopes = "resource_scopes"
	// AttrMembers is the assignment's `members` collection: the principal object
	// ids the role is granted to.
	//
	// These are emitted as BARE GUIDS, deliberately unresolved. Graph returns no
	// names here, so putting names on the record would mean a second lookup per
	// assignment against a directory scope this collector does not otherwise
	// need — and a name resolved from a different store is a name that can be
	// wrong. An unresolved guid is honest; a fabricated name is not. The guid
	// joins to entra.groups / entra.users in the backend, which already carry
	// the mapping.
	AttrMembers = "members"
)
  • What is reused rather than re-coined

Attribute keys introduced by intune.rbac (#257) — the Intune RBAC store, which is a SEPARATE role store from Entra directory roles and is invisible to entra.roles.

What is reused rather than re-coined

Most of the record already has a key in this package, and those are REUSED so an Intune role assignment and an Entra directory-role assignment answer the same LogQL filter without a translation table:

roleDefinition.id           -> AttrRoleId          ("role_id")
roleDefinition.displayName  -> AttrDisplayName / AttrRoleName
roleDefinition.description  -> AttrDescription
roleDefinition.isBuiltIn    -> AttrIsBuiltIn       ("is_built_in")
permissions[].actions       -> AttrActions         ("actions")
len(members)                -> AttrMembersCount    ("members_count")

AttrRoleId/AttrRoleName carry an INTUNE role here and an Entra directory role in entra.roles. That is the same thing (the identity of a role a principal holds) attached to two different stores, not two different things sharing a key — the #225 failure. `event_name` separates them, which is exactly the separation #257 exists to make visible.

Every key below is LOG-ONLY except the three noted as bounded gauge dimensions. `actions` in particular is an unbounded `Microsoft.Intune_*` list (121 entries on one live built-in role) and is the only field that says what a custom role can actually DO, so it must ride the twin and must never be dropped (#114).

View Source
const (
	// AttrUpdateCategory is one member of a policy's
	// autoEnrollmentUpdateCategories array (`quality`, `driver`, `feature`), and
	// the sole label on the policy gauge. A policy can enroll in several, so it
	// contributes one to each of its categories — see the gauge's description.
	AttrUpdateCategory = "update_category"
	// AttrAudienceId is the deployment-audience object a policy or deployment
	// targets (`audience.id`). Per-entity: it is a GUID naming one audience, so
	// it rides the twin and never a metric label. graph2otel does not expand the
	// audience — its members are updatable assets (devices), an unbounded
	// per-entity fan-out with its own scope story.
	AttrAudienceId = "audience_id"
	// AttrAutoEnrollmentUpdateCategories is the policy's full
	// autoEnrollmentUpdateCategories array, verbatim, so the twin still says what
	// a single policy enrolled in after the gauge has split it by member.
	AttrAutoEnrollmentUpdateCategories = "auto_enrollment_update_categories"

	// AttrComplianceChangeRuleTypes lists the @odata.type DISCRIMINATOR of each
	// complianceChangeRule on a policy, short-formed (`contentApprovalRule`).
	// Read from the discriminator, never inferred from which fields happen to be
	// present — a rule variant carries a different field set.
	AttrComplianceChangeRuleTypes = "compliance_change_rule_types"
	// AttrContentFilterTypes lists each rule's contentFilter @odata.type,
	// short-formed (`qualityUpdateFilter`, `driverUpdateFilter`). Both are
	// live-observed on one tenant, and they carry DIFFERENT fields — which is
	// exactly why the discriminator is emitted rather than guessed at.
	AttrContentFilterTypes = "content_filter_types"
	// AttrContentFilterClassifications lists the `classification` of each
	// qualityUpdateFilter (`security`, `nonSecurity`, `all`). Only the quality
	// variant has this field, so the list is shorter than
	// AttrContentFilterTypes whenever a policy mixes variants.
	AttrContentFilterClassifications = "content_filter_classifications"
	// AttrContentFilterCadences lists the `cadence` of each qualityUpdateFilter
	// (`monthly`, `outOfBand`). Quality-variant-only, like the classification.
	AttrContentFilterCadences = "content_filter_cadences"
	// AttrDeploymentStartDelays lists each rule's durationBeforeDeploymentStart
	// VERBATIM, as the ISO-8601 duration string Graph sends (`PT0S`, `P2D`). It
	// is deliberately not converted to seconds: a policy may carry several rules,
	// telemetry.Attrs has no numeric-list type, and picking one rule's delay or
	// summing them would publish a number the wire never carried.
	AttrDeploymentStartDelays = "deployment_start_delays"
	// AttrRuleLastEvaluatedDateTimes lists the lastEvaluatedDateTime of each rule
	// that has ACTUALLY been evaluated. Rules that never have carry the .NET zero
	// date (`0001-01-01T00:00:00Z`), which is omitted here and counted in
	// AttrRulesNeverEvaluated instead — emitting it would date an evaluation to
	// the year 1.
	AttrRuleLastEvaluatedDateTimes = "rule_last_evaluated_date_times"
	// AttrRulesNeverEvaluated counts the policy's complianceChangeRules whose
	// lastEvaluatedDateTime is the .NET zero date. It is the positive half of
	// dropping that sentinel: the fact "this rule has never run" survives, as a
	// number, instead of vanishing with the bogus timestamp.
	AttrRulesNeverEvaluated = "rules_never_evaluated"

	// AttrDaysUntilForcedReboot is deploymentSettings.userExperience's field of
	// the same name. It is a POINTER on the wire model: 0 means "reboot is forced
	// immediately", null means "not configured", and the two must not collapse —
	// null omits the attribute, 0 emits a real 0.
	AttrDaysUntilForcedReboot = "days_until_forced_reboot"
	// AttrOfferAsOptional is deploymentSettings.userExperience.offerAsOptional —
	// whether the update is offered as optional rather than pushed. Nullable,
	// so absent means "not configured", not false.
	AttrOfferAsOptional = "offer_as_optional"
	// AttrIsHotpatchEnabled is deploymentSettings.userExperience.isHotpatchEnabled
	// — whether quality updates apply without a reboot. Nullable.
	AttrIsHotpatchEnabled = "is_hotpatch_enabled"
	// AttrOfferWhileRecommendedBy is
	// deploymentSettings.contentApplicability.offerWhileRecommendedBy: who has to
	// recommend a driver before the policy offers it (`microsoft`).
	AttrOfferWhileRecommendedBy = "offer_while_recommended_by"

	// AttrDeploymentId is a deployment's own id. Per-entity: twin only.
	AttrDeploymentId = "deployment_id"
	// AttrDeploymentEffectiveState is state.effectiveValue — what the deployment
	// is ACTUALLY doing (`offering`, `paused`, `none`, `scheduled`). Bounded wire
	// enum, so it is a metric label.
	AttrDeploymentEffectiveState = "deployment_effective_state"
	// AttrDeploymentRequestedState is state.requestedValue — what was ASKED FOR.
	// It is a separate key, not a collapsed one, because the live wire shows the
	// two disagreeing (`effectiveValue: offering` under `requestedValue: none`)
	// and that disagreement is the signal this collector exists for.
	AttrDeploymentRequestedState = "deployment_requested_state"
	// AttrDeploymentStateReasons is state.reasons — WHY the effective state is
	// what it is. Free-form-ish and per-deployment, so twin only.
	AttrDeploymentStateReasons = "deployment_state_reasons"
	// AttrDeploymentStateMatchesRequest is the derived answer to "is this
	// deployment doing what was asked?" — "true"/"false". It exists because LogQL
	// label filters compare a label to a LITERAL, not to another label, so
	// without it the mismatch the twin's two state keys describe is not directly
	// queryable. Omitted entirely when either side is absent: an unknown value
	// cannot prove a match or a mismatch.
	AttrDeploymentStateMatchesRequest = "deployment_state_matches_request"

	// AttrUpdateContentType is the deployment's content @odata.type,
	// short-formed (`catalogContent`). Twin only — it is near-constant, so the
	// gauge breaks down by AttrCatalogEntryType instead, which says whether a
	// stuck deployment is a quality, feature or driver update.
	AttrUpdateContentType = "update_content_type"
	// AttrCatalogEntryType is the nested catalogEntry's @odata.type, short-formed
	// (`qualityUpdateCatalogEntry`, `featureUpdateCatalogEntry`). Bounded, and
	// the dimension that makes the deployment gauge worth reading.
	AttrCatalogEntryType = "catalog_entry_type"
	// AttrCatalogEntryId is the catalogEntry's id. Live-measured empty ("") on
	// this tenant's only deployment, and an empty id is not an identifier — it is
	// omitted rather than emitted, so the twin never claims to name an update it
	// cannot name.
	AttrCatalogEntryId = "catalog_entry_id"
	// AttrUpdateReleaseDateTime is the catalogEntry's releaseDateTime — when
	// Microsoft published the update, not when it was deployed.
	AttrUpdateReleaseDateTime = "update_release_date_time"
	// AttrUpdateClassification is qualityUpdateCatalogEntry.qualityUpdateClassification
	// (`security`, `nonSecurity`). Read ONLY when the catalogEntry discriminator
	// says quality — a feature entry has no such field, and reading it
	// structurally would silently map a different variant's shape.
	AttrUpdateClassification = "update_classification"
	// AttrUpdateCadence is qualityUpdateCatalogEntry.qualityUpdateCadence
	// (`monthly`, `outOfBand`). Quality-variant-only, same rule.
	AttrUpdateCadence = "update_cadence"
	// AttrIsExpeditable is catalogEntry.isExpeditable — whether this update CAN
	// be expedited at all, as opposed to whether it was.
	AttrIsExpeditable = "is_expeditable"
	// AttrIsExpedited is settings.expedite.isExpedited — whether this deployment
	// WAS expedited.
	AttrIsExpedited = "is_expedited"
	// AttrIsReadinessTest is settings.expedite.isReadinessTest — whether the
	// expedite is a rehearsal rather than a real rollout.
	AttrIsReadinessTest = "is_readiness_test"
)
  • Four keys are reused rather than re-coined
  • What is a metric label and what is not

Attribute keys introduced by intune.windows_updates (#259), the Windows Update for Business DEPLOYMENT SERVICE surface at /beta/admin/windows/updates.

Four keys are reused rather than re-coined

The two record shapes this collector maps carry four fields that already have a constant in this package, and they use those:

updatePolicy.id                  -> AttrPolicyId            ("policy_id")
*.createdDateTime                -> AttrCreatedDateTime     ("created_date_time")
deployment.lastModifiedDateTime  -> AttrLastModifiedDateTime("last_modified_date_time")
catalogEntry.displayName         -> AttrDisplayName         ("display_name")

The registry's no-duplicate-values gate makes this mandatory rather than polite: a second constant carrying "policy_id" is a build failure.

What is a metric label and what is not

Exactly four of the keys below are ever used as a METRIC label, and all four are bounded Graph wire enums whose value set does not grow with the tenant: AttrUpdateCategory, AttrDeploymentEffectiveState, AttrDeploymentRequestedState and AttrCatalogEntryType. Everything else is per-entity or unbounded twin detail — a policy id, an audience id, a rule's evaluation timestamps, a catalog entry id — and rides the log twin only (#112/#114).

View Source
const (
	AttrActionRequiredByDateTime     = "action_required_by_date_time"
	AttrActiveFileCount              = "active_file_count"
	AttrActorIds                     = "actor_ids"
	AttrAzureAdEventType             = "azure_ad_event_type"
	AttrClientIp                     = "client_ip"
	AttrDriveType                    = "drive_type"
	AttrExtendedPropertyNames        = "extended_property_names"
	AttrExtendedPropertyValues       = "extended_property_values"
	AttrExternalResharingEnabled     = "external_resharing_enabled"
	AttrFeature                      = "feature"
	AttrFeatureGroup                 = "feature_group"
	AttrFileCount                    = "file_count"
	AttrHasAttachments               = "has_attachments"
	AttrIdleSessionSignoutEnabled    = "idle_session_signout_enabled"
	AttrImpactDescription            = "impact_description"
	AttrIsDeleted                    = "is_deleted"
	AttrIsMajorChange                = "is_major_change"
	AttrIsResolved                   = "is_resolved"
	AttrLastActivityDate             = "last_activity_date"
	AttrLastModifiedDateTime         = "last_modified_date_time"
	AttrLegacyAuthEnabled            = "legacy_auth_enabled"
	AttrMessageBody                  = "message_body"
	AttrNamesConcealed               = "names_concealed"
	AttrObjectId                     = "object_id"
	AttrOperation                    = "operation"
	AttrOrganizationId               = "organization_id"
	AttrOrigin                       = "origin"
	AttrOwnerDisplayName             = "owner_display_name"
	AttrOwnerPrincipalName           = "owner_principal_name"
	AttrQuotaState                   = "quota_state"
	AttrRecordType                   = "record_type"
	AttrRecordTypeId                 = "record_type_id"
	AttrReleaseTo                    = "release_to"
	AttrRequestType                  = "request_type"
	AttrResultStatus                 = "result_status"
	AttrRootWebTemplate              = "root_web_template"
	AttrService                      = "service"
	AttrServices                     = "services"
	AttrSharingAllowedDomains        = "sharing_allowed_domains"
	AttrSharingBlockedDomains        = "sharing_blocked_domains"
	AttrSharingCapability            = "sharing_capability"
	AttrSharingDomainRestrictionMode = "sharing_domain_restriction_mode"
	AttrSiteId                       = "site_id"
	AttrSiteUrl                      = "site_url"
	AttrStorageAllocatedBytes        = "storage_allocated_bytes"
	AttrStorageRemainingBytes        = "storage_remaining_bytes"
	AttrStorageUsedBytes             = "storage_used_bytes"
	AttrUnmanagedSyncRestricted      = "unmanaged_sync_restricted"
	AttrUserKey                      = "user_key"
	AttrUserType                     = "user_type"
	AttrUserTypeId                   = "user_type_id"
	AttrVersion                      = "version"
	AttrWorkload                     = "workload"
)

Attribute keys used only by m365.* collectors.

View Source
const (
	AttrAdminAuditLogAgeLimit           = "admin_audit_log_age_limit"
	AttrAdminAuditLogEnabled            = "admin_audit_log_enabled"
	AttrLogLevel                        = "log_level"
	AttrTestCmdletLoggingEnabled        = "test_cmdlet_logging_enabled"
	AttrUnifiedAuditLogFirstOptInDate   = "unified_audit_log_first_opt_in_date"
	AttrUnifiedAuditLogIngestionEnabled = "unified_audit_log_ingestion_enabled"

	AttrAlgorithm              = "algorithm"
	AttrBodyCanonicalization   = "body_canonicalization"
	AttrDomain                 = "domain"
	AttrHeaderCanonicalization = "header_canonicalization"
	AttrIsValid                = "is_valid"
	AttrKeyCreationTime        = "key_creation_time"
	AttrLastChecked            = "last_checked"
	AttrRotateOnDate           = "rotate_on_date"
	AttrSelector1Cname         = "selector1_cname"
	AttrSelector1KeySize       = "selector1_key_size"
	AttrSelector2Cname         = "selector2_cname"
	AttrSelector2KeySize       = "selector2_key_size"
)

Exchange Online DKIM signing posture (#250, m365.exchange_dkim). Per-domain detail lives on the log twin (m365.exchange_dkim_config); the metric counts accepted domains by the bounded enabled x status tuple only, so none of these keys is ever a metric label.

View Source
const (
	AttrActionTypes                   = "action_types"
	AttrActivationDate                = "activation_date"
	AttrAddToRecipients               = "add_to_recipients"
	AttrApplyRightsProtectionTemplate = "apply_rights_protection_template"
	AttrBlindCopyTo                   = "blind_copy_to"
	AttrComments                      = "comments"
	AttrConditionTypes                = "condition_types"
	AttrCopyTo                        = "copy_to"
	AttrCreatedBy                     = "created_by"
	AttrDeleteMessage                 = "delete_message"
	AttrDlpPolicy                     = "dlp_policy"
	AttrExceptionTypes                = "exception_types"
	AttrExpiryDate                    = "expiry_date"
	AttrFromScope                     = "from_scope"
	AttrLastModifiedBy                = "last_modified_by"
	AttrManuallyModified              = "manually_modified"
	AttrPrependSubject                = "prepend_subject"
	AttrQuarantine                    = "quarantine"
	AttrRedirectMessageTo             = "redirect_message_to"
	AttrRedirectsMail                 = "redirects_mail"
	AttrRouteMessageOutboundConnector = "route_message_outbound_connector"
	AttrRuleErrorAction               = "rule_error_action"
	AttrRuleMode                      = "rule_mode"
	AttrSenderAddressLocation         = "sender_address_location"
	AttrSentToScope                   = "sent_to_scope"
	AttrSetAuditSeverity              = "set_audit_severity"
	AttrStopRuleProcessing            = "stop_rule_processing"
	AttrWhenChanged                   = "when_changed"
)

Exchange Online transport-rule attributes (#250, m365.exchange_transport_rules). Reused where a key already exists: AttrName and AttrDescription and AttrPriority and AttrState and AttrId (shared), AttrIsValid (above). Only state and rule_mode are ever metric labels — both are bounded enums fixed by the API. Every other key is per-rule data and appears on the log twin only (#112). AttrRuleMode is "rule_mode" rather than "mode" because "mode" is already AttrClipMode.

View Source
const (
	AttrAllowedOofType                    = "allowed_oof_type"
	AttrAutoForwardEnabled                = "auto_forward_enabled"
	AttrAutoReplyEnabled                  = "auto_reply_enabled"
	AttrCharacterSet                      = "character_set"
	AttrContentType                       = "content_type"
	AttrDeliveryReportEnabled             = "delivery_report_enabled"
	AttrDisplaySenderName                 = "display_sender_name"
	AttrIsInternal                        = "is_internal"
	AttrLineWrapSize                      = "line_wrap_size"
	AttrMeetingForwardNotificationEnabled = "meeting_forward_notification_enabled"
	AttrNdrDiagnosticInfoEnabled          = "ndr_diagnostic_info_enabled"
	AttrNdrEnabled                        = "ndr_enabled"
	AttrNonMimeCharacterSet               = "non_mime_character_set"
	AttrTargetDeliveryDomain              = "target_delivery_domain"
	AttrTnefEnabled                       = "tnef_enabled"
	AttrTrustedMailInboundEnabled         = "trusted_mail_inbound_enabled"
	AttrTrustedMailOutboundEnabled        = "trusted_mail_outbound_enabled"
	AttrUseSimpleDisplayName              = "use_simple_display_name"
	AttrWhenCreated                       = "when_created"
)

Exchange Online remote-domain attributes (#250, m365.exchange_remote_domains). Reused where a key already exists: AttrDomain and AttrIsValid (above), AttrName and AttrId (shared), AttrWhenChanged (transport rules). Only auto_forward_enabled is ever a metric label. Several of these are TRI-STATE on the wire — null means "use the default", not "off" — so the mapper omits them rather than asserting false.

View Source
const (
	AttrAccountDisabled                   = "account_disabled"
	AttrArchiveGuid                       = "archive_guid"
	AttrArchiveState                      = "archive_state"
	AttrArchiveStatus                     = "archive_status"
	AttrAuditEnabled                      = "audit_enabled"
	AttrAuditLogAgeLimit                  = "audit_log_age_limit"
	AttrComplianceTagHoldApplied          = "compliance_tag_hold_applied"
	AttrDeliverToMailboxAndForward        = "deliver_to_mailbox_and_forward"
	AttrEmailAddresses                    = "email_addresses"
	AttrExchangeGuid                      = "exchange_guid"
	AttrExternalDirectoryObjectId         = "external_directory_object_id"
	AttrForwardingAddress                 = "forwarding_address"
	AttrForwardingConfigured              = "forwarding_configured"
	AttrForwardingSmtpAddress             = "forwarding_smtp_address"
	AttrGrantSendOnBehalfTo               = "grant_send_on_behalf_to"
	AttrHiddenFromAddressLists            = "hidden_from_address_lists"
	AttrInPlaceHolds                      = "in_place_holds"
	AttrIsDirSynced                       = "is_dir_synced"
	AttrIsInactiveMailbox                 = "is_inactive_mailbox"
	AttrIsMailboxEnabled                  = "is_mailbox_enabled"
	AttrIsResource                        = "is_resource"
	AttrIsShared                          = "is_shared"
	AttrIssueWarningQuota                 = "issue_warning_quota"
	AttrLitigationHoldDate                = "litigation_hold_date"
	AttrLitigationHoldDuration            = "litigation_hold_duration"
	AttrLitigationHoldEnabled             = "litigation_hold_enabled"
	AttrLitigationHoldOwner               = "litigation_hold_owner"
	AttrMailboxPlan                       = "mailbox_plan"
	AttrMessageCopyForSendOnBehalfEnabled = "message_copy_for_send_on_behalf_enabled"
	AttrMessageCopyForSentAsEnabled       = "message_copy_for_sent_as_enabled"
	AttrPrimarySmtpAddress                = "primary_smtp_address"
	AttrProhibitSendQuota                 = "prohibit_send_quota"
	AttrProhibitSendReceiveQuota          = "prohibit_send_receive_quota"
	AttrRecipientTypeDetails              = "recipient_type_details"
	AttrRetainDeletedItemsFor             = "retain_deleted_items_for"
	AttrRetentionHoldEnabled              = "retention_hold_enabled"
	AttrSingleItemRecoveryEnabled         = "single_item_recovery_enabled"
	AttrWhenMailboxCreated                = "when_mailbox_created"
)

Exchange Online mailbox attributes (#250, m365.exchange_mailboxes). Reused where a key already exists: AttrUserPrincipalName and AttrDisplayName and AttrId and AttrSetting (shared), AttrWhenCreated (remote domains). Only recipient_type_details, forwarding_configured and audit_enabled are ever metric labels — all three bounded — and setting on the protection gauge. Every other key is per-mailbox data and appears on the log twin only (#112): a label keyed by UPN would grow one series per user.

View Source
const (
	AttrActivityBasedAuthTimeoutInterval = "activity_based_authentication_timeout_interval"
	AttrAuditDisabled                    = "audit_disabled"
	AttrCustomerLockboxEnabled           = "customer_lockbox_enabled"
	AttrDefaultAuthenticationPolicy      = "default_authentication_policy"
	AttrEwsAllowMacOutlook               = "ews_allow_mac_outlook"
	AttrEwsAllowOutlook                  = "ews_allow_outlook"
	AttrEwsApplicationAccessPolicy       = "ews_application_access_policy"
	AttrEwsEnabled                       = "ews_enabled"
	AttrFocusedInboxOn                   = "focused_inbox_on"
	AttrHierarchicalAddressBookRoot      = "hierarchical_address_book_root"
	AttrIpListBlocked                    = "ip_list_blocked"
	AttrIsDehydrated                     = "is_dehydrated"
	AttrIsMixedMode                      = "is_mixed_mode"
	AttrMessageRecallEnabled             = "message_recall_enabled"
	AttrOauth2ClientProfileEnabled       = "oauth2_client_profile_enabled"
	AttrPublicFoldersEnabled             = "public_folders_enabled"
)

Exchange Online organization-configuration attributes (#250, m365.exchange_org_config — the Get-OrganizationConfig half; the Get-AdminAuditLogConfig half is m365.exchange_audit_config above). Reused where a key already exists: AttrName and AttrDisplayName and AttrId and AttrSetting (shared). The BOOLEAN posture settings deliberately have no constants here: they are metric label VALUES on the bounded setting_enabled gauge, named from the wire field's snake_case, so adding one costs no registry entry. Only the non-boolean config below lands as twin attributes.

View Source
const (
	AttrAdminDisplayName                 = "admin_display_name"
	AttrAssociatedAcceptedDomains        = "associated_accepted_domains"
	AttrClientHostNames                  = "client_host_names"
	AttrCloudServicesMailEnabled         = "cloud_services_mail_enabled"
	AttrComment                          = "comment"
	AttrConnectorSource                  = "connector_source"
	AttrEnhancedFilteringSkipIps         = "enhanced_filtering_skip_ips"
	AttrEnhancedFilteringSkipLastIp      = "enhanced_filtering_skip_last_ip"
	AttrEnhancedFilteringSkipMailGateway = "enhanced_filtering_skip_mail_gateway"
	AttrEnhancedFilteringTestMode        = "enhanced_filtering_test_mode"
	AttrEnhancedFilteringUsers           = "enhanced_filtering_users"
	AttrIdentity                         = "identity"
	AttrRequireTls                       = "require_tls"
	AttrRestrictDomainsToCertificate     = "restrict_domains_to_certificate"
	AttrRestrictDomainsToIpAddresses     = "restrict_domains_to_ip_addresses"
	AttrScanAndDropRecipients            = "scan_and_drop_recipients"
	AttrSenderDomains                    = "sender_domains"
	AttrSenderIpAddresses                = "sender_ip_addresses"
	AttrTlsSenderCertificateName         = "tls_sender_certificate_name"
	AttrTreatMessagesAsInternal          = "treat_messages_as_internal"
	AttrTrustedOrganizations             = "trusted_organizations"
)

Exchange Online mail-flow connector attributes (#253, m365.exchange_connectors). Reused where a key already exists: AttrDirection, AttrConnectorType, AttrEnabled, AttrName, AttrId, AttrIsValid, AttrWhenCreated and AttrWhenChanged.

AttrConnectorType (reused, "connector_type") is the WIRE field — OnPremises vs Partner — and is a different axis from the inbound/outbound AttrDirection this collector labels with. The two are deliberately not folded together.

The Enhanced Filtering fields (the EF* wire prefix) describe skip-listing for connectors that sit behind a third-party mail gateway: which hops Exchange should look past when it evaluates the true sender. They are spelled out here rather than kept as "ef_*" because a two-letter wire prefix is unreadable in a query written six months later.

View Source
const (
	AttrAllAcceptedDomains            = "all_accepted_domains"
	AttrIsTransportRuleScoped         = "is_transport_rule_scoped"
	AttrIsValidated                   = "is_validated"
	AttrLastValidationTimestamp       = "last_validation_timestamp"
	AttrMtaStsMode                    = "mta_sts_mode"
	AttrRecipientDomains              = "recipient_domains"
	AttrRouteAllMessagesViaOnPremises = "route_all_messages_via_on_premises"
	AttrSenderRewritingEnabled        = "sender_rewriting_enabled"
	AttrSmartHosts                    = "smart_hosts"
	AttrSmtpDaneMode                  = "smtp_dane_mode"
	AttrTestMode                      = "test_mode"
	AttrTlsDomain                     = "tls_domain"
	AttrTlsSettings                   = "tls_settings"
	AttrUseMxRecord                   = "use_mx_record"
	AttrValidationRecipients          = "validation_recipients"
)

OUTBOUND-only connector attributes (#253). An outbound Get-OutboundConnector record is a different shape from an inbound one, not a superset of it: it carries none of RequireTls, the Sender*/Trusted*/EF* fields or TlsSenderCertificateName, and expresses TLS through AttrTlsSettings + AttrTlsDomain instead. Live-measured 2026-07-24 against the one outbound connector m7kni has ever had.

AttrSmartHosts and AttrRecipientDomains are the pair that answer WHERE THE MAIL GOES, which is the whole reason an outbound connector matters. AttrTestMode is the connector's own validation flag and is unrelated to AttrEnhancedFilteringTestMode above — different wire fields, different meanings, so deliberately not folded.

View Source
const (
	// AttrMessageTraceId is the trace record's own identity — the dedupe key.
	// It is NOT the Message-ID: one internet message fans out to one trace
	// record per recipient, each with a distinct MessageTraceId.
	AttrMessageTraceId = "message_trace_id"
	// AttrFromIp is the wire's FromIP: the sending host's address. IPv6 in the
	// live capture, so it is carried as an opaque string, never parsed.
	AttrFromIp = "from_ip"
	// AttrToIp is the wire's ToIP, the receiving host's address. It is EMPTY on
	// inbound mail (live-measured 2026-07-23) and the empty value is omitted
	// rather than stamped, so its presence itself distinguishes the directions.
	AttrToIp = "to_ip"
)
  • Only three keys are new, and that is the point

Attribute keys introduced by m365.message_trace (#254), the per-message mail flow record read over Exchange Online's Get-MessageTraceV2.

Only three keys are new, and that is the point

The record has twelve wire fields, and nine of them already have a constant in this package because defender.quarantine maps the SAME fields off the SAME transport. Those are REUSED rather than re-coined, which is what makes the two signals joinable in LogQL without a translation table — most importantly MessageId, which both collectors emit as AttrInternetMessageId, the same key defender.email* carries, so the join #254 exists for is one label name:

MessageId        -> AttrInternetMessageId  (the RFC 5322 Message-ID)
Received         -> AttrReceivedTime       (verbatim, also the event time)
SenderAddress    -> AttrSenderAddress
RecipientAddress -> AttrRecipientAddress
Subject          -> AttrSubject
Size             -> AttrSize
Status           -> AttrStatus

The registry's no-duplicate-values gate enforces this from the other direction: a second constant carrying "sender_address" is a build failure, so re-coining is not merely discouraged, it is impossible.

All three keys below are LOG-ONLY. Every one identifies a single message or a single network endpoint, so a metric labeled by any of them grows one series per message — the #112 failure this collector is most exposed to, since it sees one record per message per recipient.

View Source
const (
	// AttrTemplate is status.templateMessage.template — the STABLE enum key that
	// distinguishes parse outcomes (e.g. ..._PARSED_LOG_FILE_ALL_RELEVANT vs
	// ..._UNEXPECTED_FORMAT). Alert on this, never on the localized
	// statusMessage prose.
	AttrTemplate = "template"
	// AttrIsSuccess is status.isSuccess — the boolean parse verdict. Redundant
	// with AttrTemplate (which distinguishes WHICH failure) but both ship.
	AttrIsSuccess = "is_success"
	// AttrDataSource is the Cloud Discovery log format (e.g. GENERIC_CEF), from
	// logTypeName / templateMessage.parameters.dataSource.
	AttrDataSource = "data_source"
	// AttrLogType is the numeric MDCA log-type id (e.g. 179 for GENERIC_CEF).
	AttrLogType = "log_type"
	// AttrInputStreamId is the Cloud Discovery input stream a parse task belongs
	// to. Streams are single-digit per tenant, so this bounds the parse-health
	// gauges' cardinality by tenant shape, not tenant size (#112).
	AttrInputStreamId = "input_stream_id"
	// AttrTransactionsCount is templateMessage.parameters.transactionsCount — the
	// discovered transactions in a successful parse. A collapse to zero is the
	// "parsed fine, discovered nothing" case.
	AttrTransactionsCount = "transactions_count"
	// AttrCloudServicesCount is templateMessage.parameters.cloudServicesCount —
	// distinct cloud apps discovered in a successful parse.
	AttrCloudServicesCount = "cloud_services_count"
)

Attribute keys used only by mdca.* collectors — the Microsoft Defender for Cloud Apps Cloud-Discovery governance signal (#145). Shared keys these records also emit (file_size via AttrFileSize, state via AttrState, ingest_transport via AttrIngestTransport) are reused from the other attrs_*.go files, never redeclared. Every key is a semconv.Attr* constant (Gate B); no value duplicates another constant (Gate A).

View Source
const (
	AttrActionAfterRetention    = "action_after_retention"
	AttrApplicableTo            = "applicable_to"
	AttrBehaviorDuringRetention = "behavior_during_retention"
	AttrClosedDateTime          = "closed_date_time"
	AttrDescription             = "description"
	AttrDescriptionForAdmins    = "description_for_admins"
	AttrDescriptionForUsers     = "description_for_users"
	AttrExternalId              = "external_id"
	AttrName                    = "name"
	AttrRetentionTrigger        = "retention_trigger"
)

Attribute keys used only by purview.* collectors.

View Source
const (
	AttrEnforcementMode      = "enforcement_mode"
	AttrBindingType          = "binding_type"
	AttrManagementRuleId     = "management_rule_id"
	AttrActions              = "actions"
	AttrRuleName             = "rule_name"
	AttrBoundWorkloads       = "bound_workloads"
	AttrWhenChangedUtc       = "when_changed_utc"
	AttrWhenRulesChangedUtc  = "when_rules_changed_utc"
	AttrLastModifiedUtc      = "last_modified_utc"
	AttrSensitiveInfoTypeIds = "sensitive_info_type_ids"
	AttrMinConfidence        = "min_confidence"
	AttrMaxConfidence        = "max_confidence"
	AttrMinCount             = "min_count"
	AttrMaxCount             = "max_count"
)

Attribute keys for the purview.dlp_* collector (#246): DLP policy definition inventory + enforcement mode. Reuses AttrWorkload ("workload"), AttrAction ("action"), AttrEnabled ("enabled"), AttrSeverity ("severity"), AttrPolicyId ("policy_id"), AttrPolicyName ("policy_name") and AttrRuleId ("rule_id") from the other domains rather than redefining those values. AttrEnforcementMode is deliberately NOT "mode" — that value is already AttrClipMode — and reads better anyway (Enforce vs AuditAndNotify is an enforcement mode).

View Source
const (
	AttrActivity              = "activity"
	AttrAppId                 = "app_id"
	AttrCategory              = "category"
	AttrContainer             = "container"
	AttrCorrelationId         = "correlation_id"
	AttrDiagnosticCategory    = "diagnostic_category"
	AttrDisplayName           = "display_name"
	AttrIsMapped              = "is_mapped"
	AttrExpirationDateTime    = "expiration_date_time"
	AttrExpiryBucket          = "expiry_bucket"
	AttrId                    = "id"
	AttrKeyUsage              = "key_usage"
	AttrModifiedPropertyNames = "modified_property_names"
	AttrOperatingSystem       = "operating_system"
	AttrPriority              = "priority"
	AttrSeverity              = "severity"

	// AttrSource is a per-record provenance field carrying MICROSOFT's own
	// meanings, NOT graph2otel's transport. It holds different live values per
	// collector: which Graph endpoint a certificate came from (intune/certificates:
	// "managed_device" / "user_pfx") and Microsoft's verbatim `source` field
	// (entra/riskdetections). It is deliberately distinct from
	// AttrIngestTransport (which names graph2otel's ingest transport) and from the
	// `source: graph|blob` CONFIG key (#144). [#141]
	AttrSource          = "source"
	AttrStartDateTime   = "start_date_time"
	AttrState           = "state"
	AttrStatus          = "status"
	AttrType            = "type"
	AttrUserDisplayName = "user_display_name"

	// AttrUserId holds the classic Office 365 UserId (usually a UPN, sometimes a
	// sentinel) on both m365 transports. It is DELIBERATELY NOT
	// AttrUserPrincipalName: the value is a UPN only ~10 records in 11, so naming
	// it user_principal_name asserted something false ~9% of the time (#151). The
	// classic UserKey travels separately as AttrUserKey. Do not re-add a
	// user_principal_name alias alongside it. Also used by entra/intune collectors
	// as a generic user identifier.
	AttrUserId            = "user_id"
	AttrUserPrincipalName = "user_principal_name"
)

Attribute keys used across two or more domains (entra/intune/m365/purview).

View Source
const (
	// AttrVisibility is a team's visibility: public / private / hiddenMembership.
	// A bounded closed set, so it is safe as a metric label.
	AttrVisibility = "visibility"
	// AttrRole buckets a membership count by role: owner / member / guest. Bounded.
	AttrRole = "role"
	// AttrOwnersCount / AttrMembersCount / AttrGuestsCount are a team's membership
	// counts from teams.summary. Per-entity on the LOG twin only — never a metric
	// label (they are the metric VALUES, bucketed by role).
	AttrOwnersCount  = "owners_count"
	AttrMembersCount = "members_count"
	AttrGuestsCount  = "guests_count"
	// AttrIsArchived marks an archived team — an archived team is the desired
	// end-state, not an orphan, so it is excluded from the ownerless count but
	// still carries a log twin.
	AttrIsArchived = "is_archived"

	// AttrDistributionMethod is a Teams app's distributionMethod: a closed
	// 3-value enum store / organization / sideloaded. Bounded, safe as a metric
	// label. `sideloaded` is the actionable-bad state (an app installed outside
	// the tenant catalog / store).
	AttrDistributionMethod = "distribution_method"
	// AttrHasRscPermissions is the "true"/"false" flag for whether an installed
	// app holds any grantedResourceSpecificApplicationPermissions (RSC). Bounded
	// (two values), safe as a metric label; the grant LIST itself is per-entity
	// and lives only on the log twin.
	AttrHasRscPermissions = "has_rsc_permissions"
	// AttrRscPermissions is the list of resource-specific consent grants held by
	// an installed app (e.g. ChannelMessage.Read.Group). Per-entity → LOG twin
	// only, never a metric label. This is the entra.consent blind spot #247 is
	// about: RSC grants are consented per team, not tenant-wide, so an app-role
	// consent audit cannot see them.
	AttrRscPermissions = "rsc_permissions"
	// AttrTeamId is the parent team's id on an installed-app or channel twin, for
	// correlation back to the m365.team twin. Per-entity → LOG twin only.
	AttrTeamId = "team_id"
	// AttrTeamDisplayName is the parent team's display name on an installed-app or
	// channel twin. Per-entity → LOG twin only.
	AttrTeamDisplayName = "team_display_name"
	// AttrFilesFolderWebUrl is a channel's SharePoint files-folder URL. Per-entity
	// → LOG twin only.
	AttrFilesFolderWebUrl = "files_folder_web_url"
)

Attribute keys used by the m365.teams collector (#121). Shared keys these records also emit (display_name via AttrDisplayName, description via AttrDescription, id via AttrId) are reused, never redeclared. Every key is a semconv.Attr* constant (Gate B); no value duplicates another constant (Gate A).

View Source
const (
	// AttrIngestTransport names the transport that produced a log record:
	// "graph", "blob", "o365_activity", "audit_query" or "report_export". See
	// telemetry.Transport for the values and telemetry.WithTransport for the
	// stamping seam.
	//
	// Deliberately NOT named "source" (#141): that key already carries three
	// unrelated live meanings — which Graph endpoint a certificate came from
	// (intune/certificates: "managed_device" / "user_pfx") and Microsoft's own
	// `source` field passed through verbatim (entra/riskdetections). It is also
	// distinct from the `source: graph|blob` CONFIG key (#144), which selects a
	// transport rather than reporting one.
	//
	// Bounded (five values), so it is metric-label-safe under the cardinality
	// rule (#112) — but it is stamped on LOGS ONLY, because adding a label to an
	// existing metric changes that metric's series identity and would break
	// dashboards and alerts built on the current names (#82).
	AttrIngestTransport = "ingest_transport"
)

Data-record attribute keys, stamped by the telemetry emitter facade rather than by collectors.

Variables

This section is empty.

Functions

func MetricAdditive

func MetricAdditive(unit, kind string) bool

MetricAdditive reports whether the series of a metric with this unit and this SDK aggregation kind ("sum", "gauge" or "histogram") may be summed together.

#235's limiter uses it to decide the fate of the series it clips: an additive metric's tail is folded into a single `other` series carrying the tail's sum; a non-additive metric's tail is dropped, with the dropped count reported separately. It never fabricates an aggregate.

An unrecognized unit answers false, so the window between introducing a unit and classifying it cannot emit an invented number.

func UnitClassified

func UnitClassified(unit string) bool

UnitClassified reports whether this unit is one the additivity table understands. It is deliberately independent of MetricAdditive: a sum is additive whatever its unit, so asking MetricAdditive would let an unrecognized unit ride into the tree unnoticed on a counter. The build gate asks THIS question, so a new unit has to be classified even where its aggregation already determines the answer.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL