OSCAR Platform · Plane 2 · Telemetry Fabric

Instrument everything.
Leak nothing.

OSCAR's observability plane is a swappable, in-process fabric over OpenTelemetry that captures metrics, traces and cost from every service — and redacts at a single explicit boundary so telemetry carries no PHI by construction. This page shows the target architecture: where each part lives, how signal moves across the platform, and what is built vs. proposed.

Status
Inc 1 live in dev · Inc 2 merged (activation pending)
Backend
Azure Monitor · App Insights + LAW
Instrumentation
OTel provider abstraction (per-service lib)
PHI
Redacted at source · DR-12
Consumer
Ops Console (read-only, one contract)
The data flow

One signal, end to end

A span or metric is born inside a service, passes through the shared provider, and hits the single redacting exporter — the one gate where PHI and health-check noise are dropped. Only clean, low-cardinality signal reaches the Azure backend, gets aggregated once, and is served to the console over a read-only contract. Watch a packet make the trip.

TELEMETRY PIPELINE · emit → redact → store → aggregate → read
W3C traceparent + correlation_id ride the whole path
traceparent · correlation_id
◇
Service
services/*
emits spans · metrics · logs
❯_
obs-provider
lib/observability
in-process interface
⛨
Redacting exporter
single egress · no OTLP bridge
drops PHI + /health spans
▤
App Insights + LAW
Azure · in-BAA
store · query · KQL
∑
ca-monitoring-read
Container App
3 query classes
▧
Ops Console
GET-only
renders
Clean telemetry (IDs · durations · counts)
Governance / cost signal
PHI / health-span — dropped at the gate
traceparent + correlation_id

The gate is the load-bearing invariant: a single explicit exporter with an allow-list redactor and a health-span filter. There is no OTEL_EXPORTER_OTLP_* env-bridge — that would spawn a second, unfiltered egress and leak /health spans. One filtered door out, by construction.

The structuring decision

Two planes, one join key

Durable governance truth and high-volume sampled telemetry are kept deliberately separate — but every record in both planes carries correlation_id and practice_id, so a cost spike can always be joined back to the governed action that caused it.

Plane 1 · built

Governance / Event / WORM

The durable, ordered, replayable source of truth.

  • Service Bus — 3 topics, 35 typed event types
  • Transactional outbox + dead-letter ledger
  • WORM audit index · Sentinel PRS risk score
  • EventEnvelope with a validate_no_phi() gate
correlation_id · practice_id
Plane 2 · live in dev (Inc 1) · Inc 2 merged

Observability

Metrics · traces · cost, behind a swappable OTel provider.

  • In-process provider abstraction over OpenTelemetry
  • Single redacting exporter → Azure Monitor
  • Per-service RED metrics + LLM cost/latency
  • Redaction + health-span filter at the boundary
Component topology

Where each part lives

Most of Plane 2 is not a new server. It is one shared library imported in-process, a managed Azure backend, and in-place maturation of existing services — plus two small new deployables (ca-monitoring-read + the prober, merged in Increment 2).

obs-provider library

lib/observability/

The narrow interface (start_span, record_event, …), the redactor, the single exporter, propagation + structured logging. Feature code never imports the backend.

Code landedOBS-1

Instrumented fleet

6 services + 5 module/tool apps

Core (Inc 1): action-gateway · agent-orchestrator · delivery-service · outbox-relay · sentinel · thread-intelligence. Extended (Inc 2): rcm · scheduling · clinical · webapp · system-console. Each imports the lib + one middleware; chokepoints get spans.

Code landedOBS-2 · OBS-2-ext

Redacting exporter

in the library's egress

The one filtered door out: allow-list redaction (only IDs / enums / durations / codes / sizes / counts) + /health span drop. No OTLP env-bridge.

Code landedOBS-1

App Insights + LAW

Azure · Bicep

Workspace-based Application Insights wired to law-oscar-{env}, in-boundary under the existing Azure BAA. Diagnostic settings fan SQL/Redis/SB/KV into the workspace. Provisioned + live in dev.

Live in devOBS-4

ca-monitoring-read

new Container App

The one genuinely-new service. Fans in both planes + health + prober, computes the three query classes, and serves the OSCAR-owned read contract to the console. Merged to dev, inert pending Promotion-2 activation.

Merged · inertOBS-5 · DR-16

Standing prober

ca-monitoring-prober + webtests

Replaces the ephemeral ca-netprobe: a standing worker + native App Insights availability tests — the synthetic / secondary tier for the black-box DICOM services and external-dependency reachability. Merged to dev, inert pending Promotion-2 activation.

Merged · inertOBS-6

Live-hub → Web PubSub bridge

agent-orchestrator · LiveHubBackend

Scales the orchestrator's live agentic-trace stream off the single in-process /ws hub onto the provisioned wps-oscar-{env} for cross-replica console escalation (poll→push). Every pushed frame goes through a closed, versioned no-PHI projection (DR-12) — the load-bearing deliverable. p0 safety-interrupt designed; gated. Inert by default (LIVE_HUB_BACKEND=local) — activation is Promotion 3.

PR → feat · inertOBS-7 · DR-11
The seams

How it communicates

Context propagation

W3C traceparent is injected at every hop; the existing correlation_id stays the durable business join key and rides as a span attribute / baggage.

HTTP hops

Injected in the SDK transport and the read-proxy service-to-service headers, alongside X-Correlation-Id.

Service Bus

Carried in message application_properties (producer-side landed); consumers bind the context back via a shared extraction helper.

The read contract

OSCAR owns aggregation; the console is a thin renderer. Three GET query classes, every datum stamped with a provenance / freshness envelope. Route shapes are illustrative pending DPIM.

A
Near-real-time aggregate
GET /HealthSummary?scope=…
~300 s rolling · 9 categories + PRS overlay · live tiles
B
Time-series
GET /MetricReports/{metric}?window=…&percentiles=…
per-service / per-practice · p50/p95/p99 · charts + SLO
C
Cost-attributed
GET /CostReports?group_by=…&window=…
integer cents · by cost-category / practice / tenant
The redaction boundary · DR-12

No PHI by construction

Redaction happens at source, twice: Plane 1's validate_no_phi() refuses to construct a PHI-bearing envelope, and Plane 2's exporter enforces a default-deny allow-list before anything leaves the process. The console inherits a contract that carries none.

idsenumsdurations status codessizescounts namesDOBscontent promptstokens
The build

Sequenced in three increments

Delivered one PR per workstream onto feat/observability, then promoted to dev per increment — never one big-bang merge. Everything defaults to MONITORING_MODE=off, so partial landings are inert.

✅ Increment 1 activated in dev — 2026-07-14. App Insights (appi-oscar-dev) provisioned, the connection-string seeded, and all six first-party services flipped to MONITORING_MODE=otel with a resolving Key Vault secretRef. Live telemetry — request spans, W3C traceparent continuity, /health filtered, source-redaction (no PHI), and RED metrics — confirmed via KQL. Evidence: docs/DC/OBS-PROMOTION-1-dev-activation-evidence.md (Linear CAN-108).

▸ Increment 2 merged to dev — 2026-07-15 (PR #114), review-hardened, still inert. Adds the read-contract service (ca-monitoring-read), the standing synthetic prober (ca-monitoring-prober), module/tool-app instrumentation (rcm · scheduling · clinical · webapp · system-console), and causation through the governed-write path. Activation is Promotion 2 — a separate gated deploy that turns it on and live-validates it (Linear CAN-154). Module-app telemetry lights up on the dev deploy; the two new services need the activation step.

▸ Increment 3 (OBS-7) — PR into feat/observability, the last workstream. Lands inert. Scales the orchestrator live agentic-trace stream onto Web PubSub for cross-replica console escalation (the LiveHubBackend seam), proves no PHI at the push boundary via a closed, versioned egress projection (DR-12, the core deliverable — also closes the pre-existing /ws tee exposure), adds SLO/error-budget math + the retuned ingestion-volume & SLO-burn alerts, and formalizes the console push-handoff consumer contract. The p0 safety-interrupt is designed, gated (DR-08 v2.1 / DPIM amendment). Activation is Promotion 3 — a separate gated deploy; the no-PHI push boundary + client-subscription auth need human security review before switch-on.

INCREMENT 1Live in dev · 2026-07-14
OBS-4Azure backend + diagnostics
OBS-1OTel foundation + propagation
OBS-2Per-service instrumentation
OBS-3Plane-1 maturation + envelope
INCREMENT 2Merged · activation pending
OBS-5ca-monitoring-read + read contract
OBS-6Standing synthetic prober
OBS-2+Module-app instrumentation (5 apps)
OBS-3+Causation through governed-write
INCREMENT 3PR → feat · inert
OBS-7Real-time escalation + console handoff
For engineers The provider interface & config ❯

Feature code touches only the narrow interface; the backend is selected by config and swappable. The default is a no-op, so local and test runs emit nothing.

# lib/observability — feature code never imports OTel or the Azure exporter
from lib.observability import get_obs_provider, ATTR

obs = get_obs_provider()
with obs.start_span("governed_write") as span:
    span.set(ATTR.PRACTICE_ID, practice_id)   # keys are lib-owned constants,
    span.set(ATTR.ACTION_CLASS, action_class) # never inline strings (master §5.4)

# selected by env — off by default
MONITORING_MODE=off | otel
APPLICATIONINSIGHTS_CONNECTION_STRING=…   # delivered by OBS-4 via Key Vault

Attribute key names live in one attributes module — so adding or renaming an attribute fleet-wide is a 1–2 file change in the lib, not an edit across every service.

For engineers Chokepoints & the instrumentation contract ❯

Instrument the chokepoints first, then let per-service RED middleware cover the rest. Each of the nine console health categories maps to a concrete OSCAR source:

CategoryOSCAR sourceEmits
Availability/health/ready + proberuptime · up/down · incidents
ReliabilityPlane-1 events + DLQevents/24h · MTTR · dead-letters
PerformanceOTel traces/metricsp50/p95/p99 latency (RED)
LoadSB depth + outboxqueue depth · oldest-item age
ResourceAzure metrics + gaugesCPU/mem · pool · LLM concurrency
Token / Costorchestrator llm_callgen_ai.* tokens · cents · model
Agenticgoverned_write metrics + PRSverdicts · approval queue · blocks
Securityscanner feedvuln counts by severity
Administrationgovernance + WORMconfig-change rate · audit stream

Chokepoints: the Action Gateway governed-write path (folds in the existing Prometheus metrics), the orchestrator turn → tool_chain → llm_call span tree (adds gen_ai.* cost/latency, never content), and per-service request middleware for RED.