Skip to main content
Version: v0.12.11

Observability / OTel

PEAC records are designed to complement your observability stack, not replace it. The @peac/telemetry-otel package provides a bridge that attaches PEAC record references to OpenTelemetry spans via the peac.record.ref span attribute.

Key principle: PEAC does not ship an OTel exporter, SDK dependency, or semantic convention claim. peac.record.ref is a PEAC custom span attribute, not an official OpenTelemetry semantic convention.


How it works

When you issue a PEAC interaction record, compute its SHA-256 hash and attach it as a span attribute on the current OTel span. Auditors and compliance tools can later look up the full signed record using the hash as an anchor.

PEAC record (JWS)
|
v
sha256(jws) --> peac.record.ref span attribute
|
v
OpenTelemetry span (trace ID, span ID, timestamps, service info)

The span carries the lightweight reference. The full signed record travels separately (via PEAC-Receipt header, A2A metadata, MCP _meta, or your own storage). At audit time, the verifier fetches the record using the hash anchor from the span.


Install

Terminal
pnpm add @peac/telemetry-otel

Quick start

otel-bridge.ts
import { attachPeacRecordRef } from '@peac/telemetry-otel';
import { issue } from '@peac/protocol';
import { trace } from '@opentelemetry/api';

const span = trace.getActiveSpan();

// Issue a PEAC record
const jws = await issue({ sub: 'api-call-001', iss: 'https://api.example.com', ... }, signingKey);

// Attach the hash reference to the current OTel span
if (span) {
attachPeacRecordRef(span, jws);
// Sets: span.setAttribute('peac.record.ref', sha256(jws))
}

Span attribute

AttributeTypeValue
peac.record.refstringsha256:<64 hex chars> (SHA-256 of the compact JWS)

This is the same value as the receipt_ref used in Evidence Carrier Contract payloads. It provides a consistent anchor across spans, A2A metadata, MCP _meta, and HTTP PEAC-Receipt headers.


Privacy modes

@peac/telemetry-otel supports three privacy modes for what gets attached to spans:

ModeDefaultWhat is attached
ref_onlyYesOnly peac.record.ref (SHA-256 hash). No JWS, no claims.
summaryNopeac.record.ref + bounded claim summary (type, iss, sub hash).
fullNopeac.record.ref + full claim set. Only for non-sensitive contexts.
privacy-mode.ts
import { attachPeacRecordRef } from '@peac/telemetry-otel';

// Default: ref_only
attachPeacRecordRef(span, jws);

// Summary mode: includes type and issuer
attachPeacRecordRef(span, jws, { mode: 'summary' });

// Full mode: include all claims (only for non-sensitive contexts)
attachPeacRecordRef(span, jws, { mode: 'full' });

Use ref_only (the default) in any context where spans may be exported to shared trace backends. Only use full mode in isolated, controlled environments.


Zero OTel dependency in PEAC core

The PEAC core packages (@peac/kernel, @peac/schema, @peac/crypto, @peac/protocol) have no dependency on @opentelemetry/api or any OTel package. This is enforced by a CI boundary test.

If you do not use OpenTelemetry, you can compute the peac.record.ref value yourself:

import { createHash } from 'node:crypto';

function computeReceiptRef(jws: string): string {
const hash = createHash('sha256').update(jws, 'utf8').digest('hex');
return `sha256:${hash}`;
}

Composing with lifecycle records

PEAC lifecycle observation records and OTel spans address different concerns. Use both together:

ConcernUse
Distributed trace context (trace ID, span ID, latency, service graph)OpenTelemetry spans
Portable verifiable evidence of what happened (signed, offline-verifiable, cross-boundary)PEAC interaction records
Cross-system audit anchorpeac.record.ref span attribute links both
lifecycle-otel.ts
import { attachPeacRecordRef } from '@peac/telemetry-otel';
import { issue } from '@peac/protocol';
import { trace } from '@opentelemetry/api';

const span = trace.getActiveSpan();

// Issue a lifecycle observation record
const jws = await issue({
sub: 'urn:workflow:deploy-pipeline-001',
iss: 'https://ci.example.com',
type: 'org.peacprotocol/lifecycle-workflow-transition',
extensions: {
'org.peacprotocol/lifecycle-observation': {
event_kind: 'workflow-transition',
from_state: 'pending',
to_state: 'approved',
observed_at: new Date().toISOString(),
},
},
}, signingKey);

// Bridge to the current OTel span
if (span) {
attachPeacRecordRef(span, jws);
}

Attribute naming

peac.record.ref uses dotted OTel-convention naming. It is not registered as an official OpenTelemetry semantic convention. If OTel eventually standardizes a provenance or evidence attribute namespace, PEAC will provide a migration path.

Until then, peac.record.ref is an application-defined attribute and does not conflict with any current OTel semantic convention in the OpenTelemetry Semantic Conventions stable or experimental namespaces.


Non-goals

The OTel bridge does not:

  • Replace OpenTelemetry for distributed tracing
  • Ship an OTel exporter or collector configuration
  • Claim OTel semantic convention ownership for peac.record.ref
  • Provide real-time monitoring or alerting
  • Serve as a substitute for logging infrastructure