Skip to content

feat: add OpenTelemetry metrics for credential issuance and verification - #495

Merged
masv3971 merged 4 commits into
SUNET:mainfrom
sirosfoundation:feat/otel-metrics
Jul 29, 2026
Merged

feat: add OpenTelemetry metrics for credential issuance and verification#495
masv3971 merged 4 commits into
SUNET:mainfrom
sirosfoundation:feat/otel-metrics

Conversation

@leifj

@leifj leifj commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Closes #494

What

Adds OTel Metrics SDK instrumentation alongside existing tracing, covering the full OpenID4VCI and OpenID4VP flows.

Changes

New: pkg/metric/

  • otel.go: MeterProvider with OTLP push + Prometheus scrape reader
  • vci.go: VCI instruments (offers, tokens, credentials, notifications counters + latency histograms)
  • vp.go: VP instruments (requests, presentations, failures counters + latency histogram)
  • handler.go: Prometheus HTTP handler via OTel bridge
  • otel_test.go: Tests for all of the above

Modified

  • pkg/model/config.go: Add common.metrics config section (OTEL struct, same as tracing)
  • cmd/apigw/main.go: Wire MeterProvider + VCI metrics + /metrics handler
  • cmd/verifier/main.go: Wire MeterProvider + VP metrics
  • internal/apigw/apiv1/client.go: Add vci *metric.VCI field, update constructor
  • internal/apigw/apiv1/handlers_oauth.go: Instrument OAuthToken (counter + latency)
  • internal/apigw/apiv1/handlers_issuer.go: Instrument VCICredential (issued/failed counters + latency), VCIDeferredCredential, VCINotification
  • internal/apigw/apiv1/handlers_oidcrp.go: Instrument credential offer creation
  • internal/apigw/httpserver/service.go: Add /metrics Prometheus scrape endpoint
  • internal/verifier/apiv1/client.go: Add vp *metric.VP field, update constructor
  • internal/verifier/apiv1/handler_openid4vp.go: Instrument CreateRequestObject, HandleDirectPost

PII Safety

All labels are protocol-level only: format, grant_type, credential_config_id, source, error_class, event, status. No identifiers, session IDs, or claim values are ever recorded.

Cardinality

Worst case ~60 series for VCI + ~9 for VP. Well within Prometheus comfort zone.

Testing

  • pkg/metric has 6 tests covering construction, no-op, recording, and Prometheus exporter
  • All existing tests pass (nil-safe guards when vci/vp is not injected)
  • go build ./... and go vet ./... clean

Wire OTel Metrics SDK alongside existing tracing infrastructure:
- pkg/metric: MeterProvider with OTLP push + Prometheus scrape exporter
- pkg/metric/vci.go: VCI counters (offers, tokens, credentials, notifications)
  and histograms (token latency, issuance latency)
- pkg/metric/vp.go: VP counters (requests, presentations, failures)
  and histogram (verification latency)

Instrument key handlers:
- apigw: OAuthToken, VCICredential, VCIDeferredCredential, VCINotification,
  OIDC RP credential offer creation
- verifier: CreateRequestObject, HandleDirectPost

All metric labels use protocol-level metadata only (format, grant_type,
credential_config_id, source, error_class) — no PII is ever recorded.

Adds /metrics Prometheus scrape endpoint to apigw HTTP server.
Adds common.metrics config section (mirrors common.tracing).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces OpenTelemetry metrics instrumentation for the issuer (OpenID4VCI) and verifier (OpenID4VP) flows, adds a shared common.metrics configuration block, and exposes a Prometheus /metrics scrape endpoint in the apigw HTTP server.

Changes:

  • Add a new pkg/metric package providing MeterProvider setup (OTLP + Prometheus) and typed instruments for VCI/VP.
  • Wire metrics into apigw and verifier binaries and add handler-level metrics recording for key endpoints.
  • Extend config (common.metrics) and update dependencies to include OTel metric SDK/exporters.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
pkg/model/config.go Adds common.metrics configuration alongside tracing.
pkg/metric/otel.go Implements MeterProvider wiring (OTLP push + Prometheus reader) and exposes a scrape handler.
pkg/metric/handler.go Adds Prometheus exporter/handler wrapper for scraping.
pkg/metric/vci.go Defines VCI instruments (counters + histograms).
pkg/metric/vp.go Defines VP instruments (counters + histogram).
pkg/metric/otel_test.go Adds unit tests covering no-op mode, instrument construction, and exporter wiring.
internal/apigw/apiv1/client.go Injects VCI metrics into the apigw API client.
internal/apigw/apiv1/handlers_oauth.go Instruments token issuance counter + duration histogram.
internal/apigw/apiv1/handlers_issuer.go Instruments credential issuance/failure counters + duration histogram; adds deferred/notification counters.
internal/apigw/apiv1/handlers_oidcrp.go Instruments credential-offer creation counter.
internal/apigw/httpserver/service.go Adds /metrics endpoint (Prometheus scrape) when metrics are enabled.
cmd/apigw/main.go Wires MeterProvider + VCI instruments + /metrics handler into apigw.
internal/verifier/apiv1/client.go Injects VP metrics into the verifier API client.
internal/verifier/apiv1/handler_openid4vp.go Instruments VP request creation, verification failures, and direct_post processing.
cmd/verifier/main.go Wires MeterProvider + VP instruments into verifier.
go.mod Adds OTel metric SDK/exporters and related transitive deps.
go.sum Updates dependency checksums for the new/updated modules.
Comments suppressed due to low confidence (1)

internal/verifier/apiv1/handler_openid4vp.go:103

  • vp.presentations.received and vp.verification.duration are only recorded on the success path at the end of the handler, so failed direct_post requests are not counted as “received” and their latency is missing. This makes the metric names misleading and hides failure-side performance characteristics.
func (c *Client) HandleDirectPost(ctx context.Context, sessionID string, vpToken string, presentationSubmission any) error {
	start := time.Now()
	ctx, span := c.tracer.Start(ctx, "apiv1:handle_direct_post")
	defer span.End()

	// Get the session

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/verifier/apiv1/handler_openid4vp.go Outdated
Comment thread pkg/metric/otel.go
Comment thread cmd/apigw/main.go Outdated
Comment thread cmd/verifier/main.go Outdated
Comment thread internal/apigw/apiv1/handlers_issuer.go
Comment thread go.mod Outdated
…vendor

- Call meter.Shutdown() in cmd/apigw and cmd/verifier for clean
  metric flush on graceful shutdown
- Record IssuanceLatency on the failure path too so latency stats
  include failed requests
- Run go mod tidy + go mod vendor to fix indirect annotations
@masv3971
masv3971 requested a review from Copilot July 28, 2026 14:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

Comment thread cmd/apigw/main.go Outdated
Comment thread cmd/verifier/main.go Outdated
Comment thread internal/apigw/apiv1/client.go Outdated
leifj added 2 commits July 29, 2026 11:56
# Conflicts:
#	go.mod
#	go.sum
#	vendor/modules.txt
- Move metric.NewVCI/NewVP construction out of cmd/apigw/main.go and
  cmd/verifier/main.go and into apiv1.New() itself, in both services.
  main.go now just constructs the shared *metric.Meter and passes it
  through; each apiv1.New() builds its own domain-specific metrics
  from it. Keeps metrics wiring next to the code that uses it instead
  of scattered across main().
- Rename the ambiguous `vci`/`vp` Client fields to `vciMetrics`/
  `vpMetrics` in both apigw and verifier for clarity, and update all
  call sites.
@leifj

leifj commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Resolved conflicts against main (only go.mod/go.sum/vendor/modules.txt conflicted - pure dependency-version bumps, no app-code conflicts - resolved by taking main's versions and re-running `go mod tidy && go mod vendor`).

Addressed all 3 comments (commit 4c3066f):

  • Moved `metric.NewVCI`/`metric.NewVP` construction out of `cmd/apigw/main.go` and `cmd/verifier/main.go` into `apiv1.New()` in each service. `main.go` now just builds the shared `*metric.Meter` and passes it through; each apiv1 layer builds its own metrics from it.
  • Renamed `vci`/`vp` → `vciMetrics`/`vpMetrics` on both Client structs for clarity, updated all call sites.

`go build ./...` and `go test ./...` pass repo-wide.

@sonarqubecloud

Copy link
Copy Markdown

@masv3971
masv3971 requested a review from Copilot July 29, 2026 11:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@masv3971
masv3971 merged commit c1b756e into SUNET:main Jul 29, 2026
5 checks passed
@leifj leifj mentioned this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Prometheus/OTel metrics for credential issuance and verification

3 participants