Skip to content

add trace contex, el headers and failure replay#70

Merged
han0110 merged 8 commits into
eth-act:masterfrom
chetanyb:feat/trace-context-and-el-headers
Jul 15, 2026
Merged

add trace contex, el headers and failure replay#70
han0110 merged 8 commits into
eth-act:masterfrom
chetanyb:feat/trace-context-and-el-headers

Conversation

@chetanyb

Copy link
Copy Markdown
Contributor

Summary

  • Add W3C trace-context extraction on server requests and optional propagation from zkboost-client.
  • Enrich request/proving spans with HTTP attributes, span kind, and Zisk cluster job_id.
  • Support configurable EL HTTP headers, including validation, sensitive-value handling, and redacted logging.
  • Cache terminal proof failures and replay them to late SSE subscribers, mirroring successful-proof replay.

Failure replay uses a bounded, process-local LRU with latest-wins semantics. A later success evicts a stale failure.

Validation

  • Workspace tests and strict clippy pass.
  • Verified authenticated EL requests using custom headers.
  • Verified end-to-end trace propagation between proofessoor and zkBoost.
  • Verified failure replay across a proofessoor disconnect and reconciliation.

chetanyb and others added 6 commits July 8, 2026 21:27
Replace the default TraceLayer span with a custom MakeSpan that, when the
otel feature is enabled, extracts traceparent/tracestate from request
headers via the global propagator and sets the result as the request
span's parent. The whole existing explicit-parent chain (request span ->
handler -> request_proof -> fetch_witness / prove) now joins the caller's
distributed trace. The request span is also promoted from the debug-level
tower-http default to info level so it survives info-level filters.

Without the otel feature the custom MakeSpan only creates the span, with
no OpenTelemetry dependency.

Verified by a new otel-gated test using an in-memory span exporter that
asserts the exported request span keeps the caller's trace id and is
parented under the caller's span id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Declare an empty job_id field on the per-attempt prove span and pass that
span explicitly through zkVMInstance::prove into the zisk cluster backend,
which records the id on it as soon as the cluster assigns one, so traces
correlate a prove span with the cluster-side job. Recording on the
explicitly passed span rather than Span::current() means the id cannot
silently land elsewhere if an intermediate span is ever introduced.

Also emit an info log with the job id at creation so the correlation is
available in plain logs without the otel feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add an optional top-level el_headers map (default empty) next to
el_endpoint, applied as reqwest default headers when constructing the
ElClient, so every EL JSON-RPC request (chain config, blocks, witnesses)
carries them, e.g. for authenticated endpoints.

Since header values typically carry credentials they are kept out of any
Debug output: the parsed HeaderValues are marked sensitive, and Config
implements Debug manually so el_headers values format as "<redacted>".

Config validation fails at startup on invalid header names or values, and
rejects keys that differ only in case (header names are case-insensitive;
HashMap iteration order would otherwise decide which duplicate wins).
ElClient::new now propagates HTTP-client construction errors instead of
panicking, so all startup failures surface as errors.

Documented in the README and the example configs; covered by config
tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Behind a new otel cargo feature, inject the current tracing span's
OpenTelemetry context (traceparent/tracestate) into the headers of every
outbound request (request_proof, subscribe_proof_events, get_proof,
verify_proof) via the global propagator, using a hand-rolled
HeaderInjector mirroring ere_server_client's OtelPropagation middleware
and the server crate's HeaderExtractor. For the SSE subscription the
context is captured eagerly at call time, not at first poll. Without the
feature the helper returns an empty header map, so default builds change
no behavior and pull in no OpenTelemetry dependencies.

The crate docs call out that the calling application must install a
global text-map propagator (the OpenTelemetry default is a no-op), or no
traceparent header is emitted even with the feature enabled.

This completes the propagation chain for external callers such as
proofessoor: caller span -> zkboost-client traceparent -> zkboost server
request span -> ere zkVM server.

Verified by an otel-gated integration test that serves requests on a
local listener and asserts both a unary request and the SSE subscribe
arrive with a traceparent carrying the active span's trace id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Set otel.kind=server and basic HTTP semantic-convention attributes
(http.request.method, url.path, url.query) on the request span. They are
recorded as OTLP-only attributes, not tracing fields, so the log format
of default-feature builds is unchanged.

Also move the request-span trace-context test into its own
integration-test binary (tests/otel_request_span.rs): it installs a
process-global tracing subscriber so spans created on the server's tasks
are observed, and sharing a process with parallel tests both raced on the
global subscriber and poisoned the global callsite interest cache, which
previously required a warm-up/retry workaround. The relocated test
additionally asserts the span kind and HTTP attributes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Previously only completed proofs were cached and replayed on subscribe;
a subscriber that missed a live proof_failure broadcast could never learn
the terminal outcome of a request. Terminal failures are now cached in a
bounded LRU (same size as the completed-proof cache) and replayed to
per-root SSE subscribers exactly like completions, with unchanged event
shapes. A later successful proof for the same (root, proof_type) evicts
the stale failure entry so both outcomes are never replayed.

Event delivery on the subscribe endpoint is at-least-once with
latest-wins semantics: a replayed stale failure can still precede the
completion of a retry that has already succeeded, and subscribers must
treat the most recent terminal event per (root, proof_type) as
authoritative. This is documented on the handler and in the README.

Tests: subscribe-after-failure receives the replayed failure (handler
unit test + end-to-end re-subscribe assertions for proving error and
timeout), LRU bound eviction, cached-vs-broadcast event equality, and
completion evicting a stale failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chetanyb chetanyb changed the title Feat/trace context and el headers add trace contex, el headers and failure replay Jul 14, 2026

@han0110 han0110 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Mostly LGTM! Left some questions

Comment thread crates/client/src/lib.rs Outdated
Comment thread crates/server/src/proof.rs

@han0110 han0110 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!

@han0110
han0110 merged commit f22f62f into eth-act:master Jul 15, 2026
6 checks passed
@chetanyb
chetanyb deleted the feat/trace-context-and-el-headers branch July 15, 2026 15:59
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.

2 participants