@@ -296,21 +296,23 @@ Instrumented metrics
296296
297297Fedify records the following OpenTelemetry metrics:
298298
299- | Metric name | Instrument | Unit | Description |
300- | -------------------------------------------- | ------------- | ----------- | --------------------------------------------------------------- |
301- | ` activitypub.delivery.sent ` | Counter | ` {attempt} ` | Counts outgoing ActivityPub delivery attempts. |
302- | ` activitypub.delivery.permanent_failure ` | Counter | ` {failure} ` | Counts outgoing deliveries abandoned as permanent failures. |
303- | ` activitypub.delivery.duration ` | Histogram | ` ms ` | Measures outgoing ActivityPub delivery attempt duration. |
304- | ` activitypub.inbox.processing_duration ` | Histogram | ` ms ` | Measures inbox listener processing duration. |
305- | ` activitypub.signature.verification_failure ` | Counter | ` {failure} ` | Counts failed signature verification for inbox requests. |
306- | ` fedify.http.server.request.count ` | Counter | ` {request} ` | Counts inbound HTTP requests handled by ` Federation.fetch() ` . |
307- | ` fedify.http.server.request.duration ` | Histogram | ` ms ` | Measures inbound HTTP request duration in ` Federation.fetch() ` . |
308- | ` fedify.queue.task.enqueued ` | Counter | ` {task} ` | Counts inbox, outbox, and fanout tasks Fedify enqueued. |
309- | ` fedify.queue.task.started ` | Counter | ` {task} ` | Counts queue tasks Fedify began processing as a worker. |
310- | ` fedify.queue.task.completed ` | Counter | ` {task} ` | Counts queue tasks Fedify finished processing without throwing. |
311- | ` fedify.queue.task.failed ` | Counter | ` {task} ` | Counts queue tasks Fedify abandoned because processing threw. |
312- | ` fedify.queue.task.duration ` | Histogram | ` ms ` | Measures queue task processing duration in Fedify workers. |
313- | ` fedify.queue.task.in_flight ` | UpDownCounter | ` {task} ` | Tracks queue tasks currently in flight in this Fedify process. |
299+ | Metric name | Instrument | Unit | Description |
300+ | --------------------------------------------- | ------------- | ----------- | ----------------------------------------------------------------------------------------------- |
301+ | ` activitypub.delivery.sent ` | Counter | ` {attempt} ` | Counts outgoing ActivityPub delivery attempts. |
302+ | ` activitypub.delivery.permanent_failure ` | Counter | ` {failure} ` | Counts outgoing deliveries abandoned as permanent failures. |
303+ | ` activitypub.delivery.duration ` | Histogram | ` ms ` | Measures outgoing ActivityPub delivery attempt duration. |
304+ | ` activitypub.inbox.processing_duration ` | Histogram | ` ms ` | Measures inbox listener processing duration. |
305+ | ` activitypub.signature.verification_failure ` | Counter | ` {failure} ` | Counts failed signature verification for inbox requests. |
306+ | ` activitypub.signature.verification.duration ` | Histogram | ` ms ` | Measures signature verification duration across HTTP, Linked Data, and Object Integrity Proofs. |
307+ | ` activitypub.signature.key_fetch.duration ` | Histogram | ` ms ` | Measures public key lookup duration during signature verification. |
308+ | ` fedify.http.server.request.count ` | Counter | ` {request} ` | Counts inbound HTTP requests handled by ` Federation.fetch() ` . |
309+ | ` fedify.http.server.request.duration ` | Histogram | ` ms ` | Measures inbound HTTP request duration in ` Federation.fetch() ` . |
310+ | ` fedify.queue.task.enqueued ` | Counter | ` {task} ` | Counts inbox, outbox, and fanout tasks Fedify enqueued. |
311+ | ` fedify.queue.task.started ` | Counter | ` {task} ` | Counts queue tasks Fedify began processing as a worker. |
312+ | ` fedify.queue.task.completed ` | Counter | ` {task} ` | Counts queue tasks Fedify finished processing without throwing. |
313+ | ` fedify.queue.task.failed ` | Counter | ` {task} ` | Counts queue tasks Fedify abandoned because processing threw. |
314+ | ` fedify.queue.task.duration ` | Histogram | ` ms ` | Measures queue task processing duration in Fedify workers. |
315+ | ` fedify.queue.task.in_flight ` | UpDownCounter | ` {task} ` | Tracks queue tasks currently in flight in this Fedify process. |
314316
315317### Metric attributes
316318
@@ -332,6 +334,81 @@ Fedify records the following OpenTelemetry metrics:
332334: ` activitypub.verification.failure_reason ` , plus
333335 ` activitypub.remote.host ` when the failed signature includes a key ID.
334336
337+ ` activitypub.signature.verification.duration `
338+ : ` activitypub.signature.kind ` is always present and is one of ` http ` ,
339+ ` linked_data ` , or ` object_integrity ` . ` activitypub.signature.result ` is
340+ always present and is one of:
341+
342+ - `verified`: the signature was checked and accepted.
343+ - `rejected`: the signature was checked and refused (bad signature,
344+ key fetch failure, owner mismatch, etc.).
345+ - `missing`: no signature was present. Only `http` and `linked_data`
346+ produce this value; `object_integrity` does not, because the caller
347+ decides whether to invoke proof verification at all.
348+ - `error`: verification threw an unexpected error.
349+
350+ The duration covers the full verification path Fedify performs,
351+ *including* local key lookup and remote key fetches; the separate
352+ `activitypub.signature.key_fetch.duration` histogram lets operators
353+ subtract key lookup latency from the total to isolate the rest of the
354+ verification work (canonicalization, hashing, attribution and owner
355+ checks, cryptographic verification, etc.). Direct calls to
356+ `verifyRequest()` / `verifyRequestDetailed()`, `verifyJsonLd()`, and
357+ `verifyProof()` each emit exactly one measurement, even when the
358+ implementation retries internally after a cache mismatch. Wrappers
359+ such as `verifyObject()` emit one measurement per inner `verifyProof()`
360+ call (and none when the object has no proofs); higher-level inbox
361+ handling can perform several verification attempts in series.
362+
363+ Kind-specific optional attributes are recorded only when the value
364+ matches a small, spec-bounded set, to keep cardinality safe even when
365+ attacker-supplied JSON-LD or signature headers reach the verifier:
366+
367+ - `http_signatures.algorithm` (HTTP only) is recorded only when the
368+ parsed algorithm value is one of `rsa-sha1`, `rsa-sha256`,
369+ `rsa-sha512`, `ecdsa-sha256`, `ecdsa-sha384`, `ecdsa-sha512`,
370+ `ed25519`, or `hs2019` (draft-cavage) or one of the keys of the
371+ RFC 9421 algorithm map (`rsa-v1_5-sha256`, `rsa-v1_5-sha512`,
372+ `rsa-pss-sha512`, `ecdsa-p256-sha256`, `ecdsa-p384-sha384`,
373+ `ed25519`).
374+ - `http_signatures.failure_reason` (HTTP only, on `rejected` rows)
375+ is one of `invalidSignature` or `keyFetchError`. HTTP requests
376+ with no signature header are reported as
377+ `activitypub.signature.result=missing` and do not carry a
378+ `http_signatures.failure_reason`.
379+ - `ld_signatures.type` (Linked Data only) is recorded only for the
380+ spec-supported `RsaSignature2017` type.
381+ - `object_integrity_proofs.cryptosuite` (Object Integrity Proofs
382+ only) is recorded only for the spec-supported `eddsa-jcs-2022`
383+ cryptosuite.
384+
385+ Key IDs, actor IDs, request URLs, and object IDs are deliberately
386+ excluded from this histogram. They remain on the corresponding spans
387+ (`http_signatures.verify`, `ld_signatures.verify`,
388+ `object_integrity_proofs.verify`) for trace-level investigation.
389+
390+ ` activitypub.signature.key_fetch.duration `
391+ : ` activitypub.signature.kind ` is always present (same values as above).
392+ ` activitypub.signature.key_fetch.result ` is always present and is one
393+ of:
394+
395+ - `hit`: the public key was served by the configured `KeyCache`
396+ (which may itself be backed by a remote store such as Redis or a
397+ database; the measurement reflects whatever round trip that
398+ backend incurs).
399+ - `fetched`: the key was not in the cache and was loaded through
400+ the document loader, returning a usable key. This typically
401+ corresponds to a network fetch, but a custom document loader
402+ that serves from a local store will also fall in this bucket.
403+ - `error`: no usable key came back (HTTP failure, invalid response
404+ body, cached negative entry, thrown exception, etc.).
405+
406+ Unlike `activitypub.signature.verification.duration`, this histogram
407+ is recorded *per fetch attempt*: a verification that retries after a
408+ cache mismatch emits two key fetch measurements (typically one `hit`
409+ for the stale attempt and one `fetched` for the freshly fetched retry)
410+ alongside the single verification measurement that covers both.
411+
335412` fedify.http.server.request.count ` and ` fedify.http.server.request.duration `
336413: ` http.request.method ` and ` fedify.endpoint ` are always present.
337414 ` http.request.method ` is normalized to one of the standard HTTP methods
0 commit comments