diff --git a/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbResource.cs b/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbResource.cs
new file mode 100644
index 0000000..15b0aae
--- /dev/null
+++ b/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbResource.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+using OpenTelemetry.Resources;
+
+namespace ZB.MOM.WW.Telemetry;
+
+///
+/// Builds the shared OpenTelemetry ResourceBuilder from .
+/// Used internally by AddZbTelemetry so metrics, traces, and logs carry an identical
+/// Resource. Exposed for tests and custom pipelines.
+///
+public static class ZbResource
+{
+ ///
+ /// Returns a pre-populated with service.name,
+ /// service.namespace, service.version, site.id, node.role, and
+ /// host.name (always ). Attributes with
+ /// null values are omitted from the Resource.
+ ///
+ /// The telemetry options describing the service identity.
+ public static ResourceBuilder Build(ZbTelemetryOptions options) =>
+ Configure(ResourceBuilder.CreateDefault(), options);
+
+ ///
+ /// Applies the shared ZB.MOM.WW Resource attributes to an existing .
+ /// Internal seam so the AddZbTelemetry pipeline produces a Resource identical to
+ /// .
+ ///
+ internal static ResourceBuilder Configure(ResourceBuilder builder, ZbTelemetryOptions options)
+ {
+ builder.AddService(
+ serviceName: options.ServiceName,
+ serviceNamespace: options.ServiceNamespace,
+ serviceVersion: options.ServiceVersion);
+
+ var attributes = new List>
+ {
+ new("host.name", System.Environment.MachineName),
+ };
+
+ if (!string.IsNullOrEmpty(options.SiteId))
+ {
+ attributes.Add(new("site.id", options.SiteId));
+ }
+
+ if (!string.IsNullOrEmpty(options.NodeRole))
+ {
+ attributes.Add(new("node.role", options.NodeRole));
+ }
+
+ builder.AddAttributes(attributes);
+ return builder;
+ }
+}
diff --git a/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbTelemetryOptions.cs b/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbTelemetryOptions.cs
new file mode 100644
index 0000000..f52b1b3
--- /dev/null
+++ b/ZB.MOM.WW.Telemetry/src/ZB.MOM.WW.Telemetry/ZbTelemetryOptions.cs
@@ -0,0 +1,76 @@
+namespace ZB.MOM.WW.Telemetry;
+
+///
+/// Selects how instrumentation data is exported.
+///
+public enum ZbExporter
+{
+ ///
+ /// Prometheus scrape endpoint (default). Call app.MapZbMetrics() to mount /metrics.
+ ///
+ Prometheus,
+
+ ///
+ /// OTLP gRPC export. Set
+ /// (e.g. "http://collector:4317").
+ ///
+ Otlp,
+}
+
+///
+/// Options for AddZbTelemetry. All properties feed the shared OpenTelemetry Resource.
+///
+public sealed class ZbTelemetryOptions
+{
+ ///
+ /// Required. Short lower-case app identifier — e.g. "otopcua", "mxgateway",
+ /// "scadabridge". Populates OTel Resource service.name.
+ ///
+ public string ServiceName { get; set; } = "";
+
+ ///
+ /// Fleet-wide namespace. Default "ZB.MOM.WW". Do not override per-app.
+ /// Populates OTel Resource service.namespace.
+ ///
+ public string ServiceNamespace { get; set; } = "ZB.MOM.WW";
+
+ ///
+ /// Optional. Populate from AssemblyInformationalVersion.
+ /// Populates OTel Resource service.version.
+ ///
+ public string? ServiceVersion { get; set; }
+
+ ///
+ /// Optional. Physical or logical site identifier.
+ /// Populates OTel Resource site.id.
+ ///
+ public string? SiteId { get; set; }
+
+ ///
+ /// Optional. Node function: "central", "site", "hub", "standalone".
+ /// Populates OTel Resource node.role.
+ ///
+ public string? NodeRole { get; set; }
+
+ ///
+ /// App-specific Meter names to register with the OTel MeterProvider. Standard instrumentation
+ /// meters are added automatically (ASP.NET Core, HttpClient, runtime, process).
+ ///
+ public string[] Meters { get; set; } = [];
+
+ ///
+ /// App-specific ActivitySource names to register with the OTel TracerProvider.
+ ///
+ public string[] ActivitySources { get; set; } = [];
+
+ ///
+ /// Export path. Default Prometheus; use for a real collector.
+ ///
+ public ZbExporter Exporter { get; set; } = ZbExporter.Prometheus;
+
+ ///
+ /// Required when = .
+ /// OTLP gRPC endpoint, e.g. "http://collector:4317".
+ ///
+ public string? OtlpEndpoint { get; set; }
+}
diff --git a/ZB.MOM.WW.Telemetry/tests/ZB.MOM.WW.Telemetry.Tests/ZbResourceTests.cs b/ZB.MOM.WW.Telemetry/tests/ZB.MOM.WW.Telemetry.Tests/ZbResourceTests.cs
new file mode 100644
index 0000000..e9f5de8
--- /dev/null
+++ b/ZB.MOM.WW.Telemetry/tests/ZB.MOM.WW.Telemetry.Tests/ZbResourceTests.cs
@@ -0,0 +1,50 @@
+using OpenTelemetry.Resources;
+using ZB.MOM.WW.Telemetry;
+
+namespace ZB.MOM.WW.Telemetry.Tests;
+
+public sealed class ZbResourceTests
+{
+ [Fact]
+ public void Build_PopulatesAllResourceAttributes()
+ {
+ var options = new ZbTelemetryOptions
+ {
+ ServiceName = "otopcua",
+ ServiceNamespace = "ZB.MOM.WW",
+ ServiceVersion = "1.2.3",
+ SiteId = "site-7",
+ NodeRole = "central",
+ };
+
+ var resource = ZbResource.Build(options).Build();
+ var attributes = resource.Attributes.ToDictionary(a => a.Key, a => a.Value);
+
+ Assert.Equal("otopcua", attributes["service.name"]);
+ Assert.Equal("ZB.MOM.WW", attributes["service.namespace"]);
+ Assert.Equal("1.2.3", attributes["service.version"]);
+ Assert.Equal("site-7", attributes["site.id"]);
+ Assert.Equal("central", attributes["node.role"]);
+ Assert.Equal(Environment.MachineName, attributes["host.name"]);
+ }
+
+ [Fact]
+ public void Build_OmitsOptionalAttributes_WhenNull()
+ {
+ var options = new ZbTelemetryOptions
+ {
+ ServiceName = "mxgateway",
+ // ServiceVersion / SiteId / NodeRole left null
+ };
+
+ var resource = ZbResource.Build(options).Build();
+ var keys = resource.Attributes.Select(a => a.Key).ToHashSet();
+
+ Assert.Contains("service.name", keys);
+ Assert.Contains("service.namespace", keys);
+ Assert.Contains("host.name", keys);
+ Assert.DoesNotContain("service.version", keys);
+ Assert.DoesNotContain("site.id", keys);
+ Assert.DoesNotContain("node.role", keys);
+ }
+}