Skip to main content
CoreSDK
Observability

OpenTelemetry

Automatic distributed tracing and metrics with OTEL.

Available in Phase 1b. This feature ships with the sidecar daemon and wrapper SDKs. Phase 1a (Rust crate only) users access this via the core engine directly.

OpenTelemetry

CoreSDK automatically creates OTEL spans for every request, attaches auth and policy outcomes as span attributes, and propagates trace context across service boundaries.

Configuration

let engine = Engine::from_env().await?;
// Set via env vars:
// OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
// CORESDK_SERVICE_NAME=orders-service
# Python: set env vars — sidecar handles OTLP export
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# CORESDK_SERVICE_NAME=orders-service

from coresdk.tracing.decorator import trace

@app.get("/products")
@trace(intent="list-products")
async def list_products(request: Request):
    ...
// Phase 2 — Go SDK not yet available
sdk, err := sdk.New(sdk.Config{
    Tenant:          "acme",
    OtelEndpoint:    "http://localhost:4317",   // OTLP gRPC
    OtelServiceName: "orders-service",
    OtelSampleRate:  1.0,                       // 0.0–1.0
})
// Phase 2 — TypeScript SDK not yet available
const sdk = new CoreSDK({
    tenant: "acme",
    otelEndpoint: "http://localhost:4317",    // OTLP gRPC
    otelServiceName: "orders-service",
    otelSampleRate: 1.0,                      // 0.0–1.0
});

What gets traced

Every request span includes:

AttributeExample
coresdk.tenant_idacme
coresdk.user_idusr_2xK9
coresdk.user_rolemember
coresdk.auth.methodjwt
coresdk.auth.resultallowed
coresdk.policy.actionorders:read
coresdk.policy.resultallowed
coresdk.policy.latency_us45
http.methodGET
http.route/api/orders
http.status_code200

Live trace viewer

core trace tail --tenant acme
# stream live traces to terminal

core trace tail --tenant acme --filter 'policy.result=denied'
# filter to denied requests only

Custom spans

use coresdk_engine::telemetry::span;

async fn process_order(order: Order, user: &User) {
    let _span = span!("process_order",
        order.id = %order.id,
        user.id = %user.id,
    );

    // span automatically ends when _span drops
    do_work(&order).await;
}
from coresdk.tracing.decorator import trace

@trace(intent="process-order")
async def process_order(order: Order, user: User):
    # span automatically created and ended by the decorator
    await do_work(order)
import "github.com/coresdk/sdk/telemetry"

func processOrder(ctx context.Context, order Order, user User) error {
    ctx, span := telemetry.Start(ctx, "process_order",
        telemetry.Attr("order.id", order.ID),
        telemetry.Attr("user.id", user.ID),
    )
    defer span.End()

    // span automatically ends when deferred
    return doWork(ctx, &order)
}
import { span } from "@coresdk/sdk/telemetry";

async function processOrder(order: Order, user: User): Promise<void> {
    await span("process_order", { "order.id": order.id, "user.id": user.id }, async () => {
        // span automatically ends when callback resolves
        await doWork(order);
    });
}

Baggage propagation

CoreSDK automatically propagates user and tenant context as OTEL baggage across service boundaries. Downstream services using CoreSDK will have the same user_id and tenant_id in their spans.

Metrics

CoreSDK exports the following OTEL metrics:

MetricTypeDescription
coresdk.auth.requestsCounterAuth requests by result (allowed/denied)
coresdk.auth.latencyHistogramJWT verification latency
coresdk.policy.requestsCounterPolicy evaluations by action + result
coresdk.policy.latencyHistogramRego evaluation latency
coresdk.errorsCounterErrors by type

CloudEvents envelope

Phase note. Ships Phase 2.

CoreSDK can wrap OTel events in a CloudEvents metadata envelope before exporting. This enables interoperability with event-driven systems (Kafka, EventBridge, Knative Eventing, Azure Event Grid) that expect CloudEvents format.

let engine = Engine::from_env().await?;
// cloud_events configuration via coresdk-sidecar.yaml
# Phase 2 — configure via sidecar YAML
# cloud_events:
#   enabled: true
#   source: https://orders.acme.com
// Phase 2 — Go SDK not yet available
sdk, err := coresdk.New(coresdk.Config{
    Tenant:            "acme",
    CloudEvents:       true,
    CloudEventsSource: "https://orders.acme.com",
})
// Phase 2 — TypeScript SDK not yet available
const sdk = new CoreSDK({
    tenant: "acme",
    cloudEvents: true,
    cloudEventsSource: "https://orders.acme.com",
});

Each exported OTel log event is wrapped in a CloudEvents envelope:

{
  "specversion": "1.0",
  "type": "io.coresdk.policy.decision",
  "source": "https://orders.acme.com",
  "id": "01HX7KQMB4NWE9P6T2JS0RY3ZV",
  "time": "2026-03-19T14:35:00Z",
  "datacontenttype": "application/json",
  "data": {
    "tenant_id": "acme",
    "user_id": "usr_2xK9",
    "action": "orders:read",
    "result": "allowed",
    "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}

CloudEvents wrapping applies to log events only — traces and metrics continue to export via OTLP in standard format.


Export targets

CoreSDK works with any OTLP-compatible backend:

// Jaeger
.otel_endpoint("http://jaeger:4317")

// Grafana Tempo
.otel_endpoint("http://tempo:4317")

// Datadog (via OTLP)
.otel_endpoint("http://datadog-agent:4317")

// CoreSDK Cloud
.otel_endpoint("https://otel.coresdk.dev")
.otel_api_key("sdk_...")
# Set OTEL_EXPORTER_OTLP_ENDPOINT to route to any OTLP-compatible backend:

# Jaeger
# OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317

# Grafana Tempo
# OTEL_EXPORTER_OTLP_ENDPOINT=http://tempo:4317

# Datadog (via OTLP)
# OTEL_EXPORTER_OTLP_ENDPOINT=http://datadog-agent:4317

# CoreSDK Cloud
# OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.coresdk.dev
// Jaeger
sdk.New(sdk.Config{Tenant: "acme", OtelEndpoint: "http://jaeger:4317"})

// Grafana Tempo
sdk.New(sdk.Config{Tenant: "acme", OtelEndpoint: "http://tempo:4317"})

// Datadog (via OTLP)
sdk.New(sdk.Config{Tenant: "acme", OtelEndpoint: "http://datadog-agent:4317"})

// CoreSDK Cloud
sdk.New(sdk.Config{
    Tenant:      "acme",
    OtelEndpoint: "https://otel.coresdk.dev",
    OtelApiKey:   "sdk_...",
})
// Jaeger
new CoreSDK({ tenant: "acme", otelEndpoint: "http://jaeger:4317" })

// Grafana Tempo
new CoreSDK({ tenant: "acme", otelEndpoint: "http://tempo:4317" })

// Datadog (via OTLP)
new CoreSDK({ tenant: "acme", otelEndpoint: "http://datadog-agent:4317" })

// CoreSDK Cloud
new CoreSDK({
    tenant: "acme",
    otelEndpoint: "https://otel.coresdk.dev",
    otelApiKey: "sdk_...",
})

On this page