OpenTelemetry GenAI Semantic Conventions: Asqav Receipt Mapping
OpenTelemetry GenAI semantic conventions define the de-facto schema for observing LLM and agent operations in distributed systems. They tell you what happened and when. An Asqav compliance receipt records the same action in a signed, externally-anchored envelope that a third party can verify offline without trusting the operator.
This page maps OTel GenAI span attributes to Asqav receipt fields so teams running OTel instrumentation can understand what the receipt adds, and so compliance engineers can correlate a receipt to its matching OTel span.
OTel GenAI attributes on this page are drawn from the OpenTelemetry GenAI Semantic Conventions specification, maintained at github.com/open-telemetry/semantic-conventions-genai. Attribute names (
Source: OpenTelemetry GenAI Semantic Conventions, https://github.com/open-telemetry/semantic-conventions-genaigen_ai.operation.name,gen_ai.provider.name,gen_ai.request.model,gen_ai.response.model,gen_ai.usage.input_tokens,gen_ai.usage.output_tokens,gen_ai.tool.name,gen_ai.conversation.id,gen_ai.response.id,gen_ai.response.finish_reasons,gen_ai.tool.call.id) are cited verbatim from that specification.
What OTel gives you vs what a receipt adds
OTel telemetry is mutable, operator-held, and intended for live observability. An Asqav receipt is:
- Signed: the signing key is a post-quantum ML-DSA-65 (FIPS 204) key; the signature covers a canonical-JSON hash of the receipt fields.
- Externally anchored: optional RFC 3161 and OpenTimestamps witnesses bind the receipt to an external time-chain, so the timestamp cannot be back-dated by the operator.
- Verifiable offline: anyone with the public key can verify the signature without an API call, using the open-source verifier (
asqavon PyPI,@asqav/sdkon npm). - Immutable after issuance: no field in a signed receipt can be changed without invalidating the ML-DSA-65 signature.
OTel gives you mutable observability. The receipt gives you a signed, externally-anchored record of the same action that a third party can verify offline. They are complementary, not competing.
Trace correlation
Asqav carries a trace_correlation capability (listed in /.well-known/governance.json). You can embed the OTel trace_id and span_id into the receipt's action_id or as a producer-asserted field, so the signed receipt and the OTel span are linked by the same identifiers. This lets an incident responder move from an OTel trace in a backend like Jaeger or Tempo directly to the signed receipt covering that span, or vice versa.
The otel_span_export capability (also in governance.json) means Asqav can export receipt events as OTel spans to an OTLP-compatible collector, keeping both signals in the same observability pipeline.
Field-by-field mapping: inference spans
The table below maps OTel inference span attributes (gen_ai.inference.client span kind) to their corresponding Asqav receipt fields.
| OTel attribute | OTel meaning | Asqav receipt field | What the receipt adds |
|---|---|---|---|
gen_ai.operation.name |
Name of the operation (e.g. chat, text_completion) |
action_type (e.g. llm:egress) |
Signed into the receipt envelope; cannot be altered post-issuance |
gen_ai.provider.name |
Provider identifier (e.g. openai, anthropic) |
Producer-asserted field in the signed payload | Binding at signing time; verifier can confirm the provider claim was not changed after the fact |
gen_ai.request.model |
Model name sent in the request | Producer-asserted field in the signed payload | Proves which model was requested at T; authored_by.model_id and authored_by.model_version bind model identity on code-authorship receipts |
gen_ai.response.model |
Actual model that generated the response | authored_by.model_id / authored_by.model_version (on applicable receipt types) |
authored_by.attestation_source is required whenever a model id is asserted, so the value is never read as Asqav-verified attribution |
gen_ai.usage.input_tokens |
Tokens consumed in the prompt | Producer-asserted field in the signed payload | Token counts are recorded as producer-asserted telemetry; the signed envelope proves the count was not altered |
gen_ai.usage.output_tokens |
Tokens generated in the response | Producer-asserted field in the signed payload | Same tamper-evidence property as input tokens |
gen_ai.response.id |
Unique completion identifier returned by the provider | action_id or producer-asserted correlation field |
Binding at signing time; lets a verifier link the receipt to the provider's completion record |
gen_ai.response.finish_reasons |
Why the model stopped generating (e.g. stop, length) |
Producer-asserted field in the signed payload | Proves the termination reason was asserted at T |
gen_ai.conversation.id |
Conversation or session identifier | session_id / producer-asserted field |
Sessions chain receipts into a traceable audit trail; session identifiers are signed into each receipt |
gen_ai.request.temperature, gen_ai.request.top_p, gen_ai.request.max_tokens |
Sampling parameters | Producer-asserted fields in the signed payload | Proves the inference configuration in use at signing time |
Field-by-field mapping: tool execution spans
The table below maps OTel tool execution span attributes (execute_tool span kind) to Asqav receipt fields.
| OTel attribute | OTel meaning | Asqav receipt field | What the receipt adds |
|---|---|---|---|
gen_ai.tool.name |
Name of the tool being executed | action_type (e.g. tool:search, function:call) + tool_fingerprint |
tool_fingerprint is a 32-hex SHA-256 over tool_name|schema_canonical_json|module_path; it surfaces registry-shadow drift and naming collisions |
gen_ai.tool.call.id |
Tool call identifier | action_id or producer-asserted correlation field |
Binding at signing time |
gen_ai.tool.description |
Tool description | tool_fingerprint (derived from schema) |
The fingerprint covers the tool's schema, not free-text description |
gen_ai.tool.type |
Tool type (function, extension, datastore) |
action_type namespace (e.g. function:call, data:read) |
Controlled vocabulary in the receipt enforces consistent classification |
gen_ai.tool.call.result |
Tool output | result_digest (sha256:<64 hex>) |
SHA-256 of the canonical-JSON tool response body; the receipt binds the exact output, not just a label |
The gap OTel cannot fill
OTel spans live in the operator's telemetry backend. The operator controls the backend, can modify or delete spans, and holds the sole copy. That is fine for live observability. It is not sufficient when a regulator, auditor, or counterparty asks: "prove this agent acted within its mandate at time T, and prove you have not altered the record."
Asqav provides verifiable evidence for that gap with four properties OTel does not provide:
- Cryptographic signing (ML-DSA-65, FIPS 204): the receipt signature covers a canonical hash of all fields. Any field change breaks the signature.
- External time anchoring (RFC 3161 + OpenTimestamps): a witness holds an inclusion proof tying the receipt to an external chain; the operator cannot back-date the timestamp.
- Operator-independent verification: the public key is at
/.well-known/jwks.json; any party can verify without contacting Asqav and without trusting the operator. - Chain of custody: receipts chain via a hash-chain link; the full audit trail can be replayed and each link verified.
Capture topology for OTel-instrumented systems
Teams that already instrument with OTel can add the receipt layer without replacing their telemetry pipeline. The passive_telemetry capture topology (capture_topology field in governance.json) lets Asqav observe the same events the OTel SDK observes, emit a signed receipt, and forward the OTel span to the existing OTLP collector. The two pipelines run in parallel.
The in_process_sdk topology is the default for Python and TypeScript agents; the OTel SDK and the Asqav SDK can both run in the same process and share the trace context.
What Asqav does not do
Asqav does not replace an OTel collector, an observability backend, or a tracing library. It does not validate OTel attribute values, re-compute token counts, or verify that a gen_ai.response.model field matches what the provider actually used. Producer-asserted fields in a receipt carry the same trust level as OTel attributes: the producer declared them. The difference is that Asqav's signing step means that declaration cannot be changed after issuance without breaking the signature.