Skip to main content

Config schema

Complete schema for ~/.hermes/hermes_otel.yaml (or a legacy ~/.hermes/plugins/hermes_otel/config.yaml, or the file named by HERMES_OTEL_CONFIG). See config.yaml for the narrative version.

The field table below is generated from HermesOtelConfig by scripts/gen_config_docs.py; a test fails if it drifts.

Top level​

FieldTypeDefaultDescription
enabledbooltrueMaster kill switch; false unloads every hook
sample_ratefloat | nullnullParent-based trace-ID ratio 0.0–1.0; null = AlwaysOn (no sampling)
root_span_ttl_msint600000Orphan-sweep TTL: a turn root older than this with no end hook is closed
flush_interval_msint60000Metrics export cadence (PeriodicExportingMetricReader)
preview_max_charsint1200Cap on preview strings (tool args/results, user message, assistant response)
capture_previewsbooltrueDeprecated spelling of content_capture: off (when false); kept consistent with content_capture
tool_input_preview_max_charsint | nullnullPer-category cap for tool args previews; null = preview_max_chars
tool_output_preview_max_charsint | nullnullPer-category cap for tool result previews; null = preview_max_chars
llm_input_preview_max_charsint | nullnullPer-category cap for LLM input previews; null = preview_max_chars
llm_output_preview_max_charsint | nullnullPer-category cap for LLM output previews; null = preview_max_chars
headersmap(unset)Extra HTTP headers on every OTLP request; per-backend headers: are merged onto these
global_tagsmap(unset)Merged into the OTel Resource; overridden by resource_attributes on key conflict
resource_attributesmap(unset)Merged into the Resource on top of the defaults service.name=hermes-agent, service.instance.id (per-process UUID), service.version, process.pid
project_namestring | null(unset)openinference.project.name on the Resource (Phoenix project); overrides OTEL_PROJECT_NAME
span_batch_max_queue_sizeint2048Max buffered spans per backend before drops
span_batch_schedule_delay_msint1000BatchSpanProcessor worker wake-up cadence
span_batch_max_export_batch_sizeint | nullnullMax spans per OTLP POST; null = 512, or 64 when content_capture is full
span_batch_export_timeout_msint30000Per-export HTTP timeout
force_flush_on_session_endbooltrueFlush every backend's span queue at the end of each turn, from a background thread (500 ms per backend, coalesced)
force_flush_wait_msint1500How long the turn waits for that background flush (spans, then metrics and logs on a second thread) before returning to Hermes; a one-shot run exits right after, so this bounds what it exports; 0 = do not wait
capture_conversation_historyboolfalseAttach the full message JSON to llm.* spans
conversation_history_max_charsint20000JSON cap when conversation capture is on
content_capturestring"full"full (default): complete prompt and response on every api.* span · preview: clipped previews only · off: no content, metadata only; see Conversation capture
capture_full_promptsbooltrueDeprecated: derived from content_capture; false keeps prompts as previews in full mode
capture_full_responsesbooltrueDeprecated: derived from content_capture; false keeps responses as previews in full mode
capture_sender_idboolfalseGateway sessions add hermes.sender.id and user.id (platform:sender)
capture_logsboolfalseAttach an OTel LoggingHandler to Python logging; see OTel logs
log_levelstring"INFO"Handler level: DEBUG / INFO / WARNING / ERROR / CRITICAL
log_attach_loggerstring | null(unset)Logger to attach to; null = root, hermes_otel = the plugin only
log_exclude_loggerslist of strings (env: comma-separated)('opentelemetry', 'urllib3', 'httpx', 'httpcore', 'requests', 'openai', 'asyncio', 'hpack', 'grpc', 'websockets', 'charset_normalizer', 'markdown_it')Logger-name prefixes never forwarded (loop guard + Hermes' noisy third-party loggers); logs.exclude_loggers
log_logger_levelsmap(unset)Per-logger minimum level, e.g. {gateway.config: WARNING}; logs.logger_levels
log_only_in_turnboolfalseDrop records the plugin cannot attribute to a session; logs.only_in_turn
log_max_attribute_lengthint4096Longest exported string attribute on a log record (characters); logs.max_attribute_length
log_batch_schedule_delay_msint1000Log batch processor: export every N ms; logs.batch.schedule_delay_ms
log_batch_max_queue_sizeint2048Log batch processor: queued records before drops; logs.batch.max_queue_size
log_batch_max_export_batch_sizeint512Log batch processor: records per export request; logs.batch.max_export_batch_size
log_batch_export_timeout_msint30000Log batch processor: per-export timeout; logs.batch.export_timeout_ms
log_live_min_levelstring"INFO"Floor for the dashboard's live Logs tab (INFO default; the OTLP path keeps log_level); logs.live_min_level
log_eventsboolfalseEmit structured hermes.* / GenAI events from the hooks (Phase 2); logs.events.enabled
log_events_contentstring"inherit"Content on events: inherit (follow content_capture) / full / preview / off; logs.events.content
emit_genai_metricsbooltrueAlso emit the OTel GenAI spec metrics (gen_ai.client.*, gen_ai.agent.*)
metrics_temporalitystring | null(unset)Metric temporality for OTLP export: cumulative (Prometheus family) or delta (Datadog, New Relic, Logfire); unset = SDK default; a backend entry's own value wins
metrics_histogramstring"explicit"Histogram aggregation: explicit (spec bucket boundaries) or exponential (base-2, for backends that accept it)
metrics_label_limitint100Distinct values a metric label such as model may take per process before the rest fold into other (0 = no cap)
skill_spansbooltrueOpen a skill.<name> span on each successful skill load, closed at turn end
discovery_promptboolfalseRegister a system-prompt section advertising hermes_otel:observability (changes what the model sees every turn; opt-in)
dashboard_livebooltrueKeep recent spans/metrics/logs in $HERMES_HOME/hermes_otel_live.db for the dashboard's Live mode
dashboard_live_max_spansint1000Rows kept per kind (spans, metrics, logs) in the live store
dashboard_live_retention_hoursfloat168.0Rows older than this are dropped from the live store (0 = only the row cap applies)
host_metricsboolfalseSample CPU/GPU and emit process.* / system.* / hw.* metrics; see Host & GPU metrics
host_metrics_gpustring"auto"auto · amd · nvidia · off — which GPU SDK to probe
host_metrics_interval_msint1000Host sampling cadence (floor 50 ms)
suppress_mcp_ping_spansbooltrueDrop successful MCP keepalive ping spans before export
backendslist(unset)Multi-backend fan-out list; see the backends[] section

When backends: is present and non-empty, single-backend env-var detection is skipped.

backends[] entries​

Shared fields (all optional unless noted):

FieldTypeDescription
typestringRequired. One of: phoenix, langfuse, signoz, jaeger, tempo, otlp, lgtm, uptrace, openobserve, parseable, honeycomb, weave. (LangSmith is env-var only — LANGSMITH_TRACING=true; it is not an OTLP backend.)
namestringFriendly name shown in logs (default: type)
endpointstringFull OTLP traces endpoint URL. Required for every type except langfuse (built from base_url), honeycomb (built from region) and weave (built from base_url)
ui_urlstringWhere the backend's web UI is, for the dashboard's Settings tab to link its card to. Optional: without it the tab derives the link from endpoint for the types whose UI is served on the OTLP origin (Phoenix, Langfuse, OpenObserve, Uptrace, Parseable), from query_port for Jaeger and SigNoz, and from a port-less endpoint host for the rest; it never guesses a port
tracesboolOverride trace-export default (true). Set false for dashboard/query-only backends that should not receive span exports. trace is accepted as an alias.
metricsboolOverride metrics-export default for this backend
metrics_temporalitystringcumulative or delta for this backend's metric reader; overrides the top-level default and the type preset (signoz, uptrace → delta)
logsboolOverride logs-export default (on for signoz, otlp, lgtm, uptrace, openobserve, parseable, honeycomb; off elsewhere)
headersmapPer-backend HTTP headers (merged onto top-level headers)

Type-specific fields​

phoenix​

FieldTypeDescription
endpointstringRequired. e.g. http://localhost:6006/v1/traces. Traces only (Phoenix rejects /v1/metrics)

langfuse​

FieldTypeDescription
public_keystringLangfuse public key (inline; discouraged)
public_key_envstringEnv var name holding the public key
secret_keystringLangfuse secret key (inline; discouraged)
secret_key_envstringEnv var name holding the secret key
base_urlstringLangfuse base URL (e.g. https://cloud.langfuse.com); the plugin appends /api/public/otel/v1/traces
query_apistringDashboard read path: v3 (trace endpoints), v4 (events_only: v2 observations + v2 metrics), or auto (default: probed once per query URL)
endpointstringOverride; skips base_url construction

Basic Auth header is constructed automatically from public + secret keys.

signoz​

FieldTypeDescription
endpointstringOTLP endpoint (self-host: http://localhost:4328/v1/traces; cloud: https://ingest.<region>.signoz.cloud:443/v1/traces)
ingestion_keystringSigNoz Cloud ingestion key (inline; discouraged)
ingestion_key_envstringEnv var name holding the ingestion key
query_portintDashboard read path: the SigNoz UI/API port (default 3301; 443 behind an HTTPS ingress)
api_key / api_key_envstringDashboard read path: a SigNoz API key, sent as SIGNOZ-API-KEY (current SigNoz issues them from a service account). Ingestion never needs it

When an ingestion key is set, the plugin adds the signoz-ingestion-key header.

jaeger​

FieldTypeDescription
endpointstringRequired. e.g. http://localhost:4318/v1/traces. Traces only (metrics/logs off)
query_portintDashboard read path: the Jaeger UI/query port (default 16686)
query_apistringDashboard read path: v1 (classic /api/traces), v3 (Jaeger v2's /api/v3), or auto (default: probed once per query URL)
service_namestringDashboard read path: the service every search names (default: the plugin's resource_attributes.service.name, else hermes-agent)

tempo​

FieldTypeDescription
endpointstringRequired. Traces only (metrics/logs off); use type: lgtm for the all-in-one Grafana container
query_portintDashboard read path: Tempo's query API port (default 3200)
prometheus_url / loki_urlstringOptional. Naming them gives a Tempo entry the same dashboard metrics/logs as lgtm
search_fetchintDashboard read path: traces asked of Tempo per search page before the newest limit are kept (default 500; Tempo's own limit is not newest-first). Also on lgtm entries

otlp​

FieldTypeDescription
endpointstringRequired. OTLP traces endpoint
metricsboolDefault: true; set false for traces-only collectors
logsboolDefault: true; set false if the collector doesn't accept /v1/logs

Use headers: for auth (${VAR} interpolation supported).

lgtm​

Alias over otlp with a dedicated display name and all signals on by default. See Grafana LGTM.

FieldTypeDescription
endpointstringRequired. OTLP traces endpoint — http://localhost:4318/v1/traces for the bundled docker-compose/lgtm/docker-compose.yaml
metricsboolDefault: true
logsboolDefault: true
query_portintDashboard read path: Tempo's query API port (default 3200)
prometheus_urlstringDashboard metrics. Default: port 9090 on the Tempo host (the otel-lgtm layout); off disables
loki_urlstringDashboard logs. Default: port 3100 on the Tempo host; off disables (e.g. a Loki without OTLP)
metrics_matchstringOptional Prometheus series selector for the instrument list, e.g. {__name__=~"hermes_.*"} on a shared Prometheus
loki_selectorstringOptional LogQL stream selector the log filters are applied to (default {service_name=~".+"})

Use type: lgtm (not type: tempo) when pointing at the grafana/otel-lgtm container — tempo is traces-only and would disable the logs/metrics fan-out.

uptrace​

FieldTypeDescription
endpointstringRequired. OTLP traces endpoint (self-host: http://localhost:14318/v1/traces). Also via OTEL_UPTRACE_ENDPOINT
dsnstringUptrace DSN, sent as the uptrace-dsn header on every export (inline; discouraged)
dsn_envstringEnv var name holding the DSN (falls back to OTEL_UPTRACE_DSN / UPTRACE_DSN)
query_portintDashboard read path: the Uptrace UI/API port (default 14318; 443 behind an HTTPS ingress)
user_token / user_token_envstringDashboard read path: an Uptrace user token (Settings → API tokens); falls back to UPTRACE_USER_TOKEN. The DSN's project token only ingests
project_idintDashboard read path: the Uptrace project to query (default 1)

All three signals on by default. See Uptrace.

openobserve​

FieldTypeDescription
endpointstringRequired. http://<host>:5080/api/<org>/v1/traces. Also via OTEL_OPENOBSERVE_ENDPOINT
user / user_envstringBasic-auth user (falls back to OTEL_OPENOBSERVE_USER / OPENOBSERVE_USER)
password / password_envstringBasic-auth password (falls back to OTEL_OPENOBSERVE_PASSWORD / OPENOBSERVE_PASSWORD)
stream_namestringOptional stream name (OTEL_OPENOBSERVE_STREAM)

All three signals on by default. See OpenObserve.

parseable​

FieldTypeDescription
endpointstringRequired. Parseable ingestor OTLP traces endpoint, ending in /v1/traces
api_keystringParseable API key (inline; discouraged)
api_key_envstringEnv var holding the key (falls back to PARSEABLE_API_KEY / OTEL_PARSEABLE_API_KEY)
traces_datasetstringTraces dataset (default: hermes-traces)
metrics_datasetstringMetrics dataset (default: hermes-metrics)
logs_datasetstringLogs dataset (default: hermes-logs)

The plugin supplies signal-specific X-P-Stream and X-P-Log-Source headers automatically. See Parseable.

honeycomb​

FieldTypeDescription
api_keystringHoneycomb ingest key (inline; discouraged)
api_key_envstringEnv var name holding the key (falls back to OTEL_HONEYCOMB_API_KEY / HONEYCOMB_API_KEY)
regionstringus (default) or eu; selects the endpoint when none is given
datasetstringOptional x-honeycomb-dataset header. Only honored by Classic keys — modern Environments keys ignore it
endpointstringOverride; skips the region default. Also via OTEL_HONEYCOMB_ENDPOINT

The plugin sets x-honeycomb-team from the key automatically and enables all three signals. See Honeycomb for dataset routing details.

weave​

FieldTypeDescription
api_keystringW&B API key (inline; discouraged)
api_key_envstringEnv var name holding the key (falls back to OTEL_WEAVE_API_KEY / WANDB_API_KEY)
entitystringW&B entity/team; copied to Resource attribute wandb.entity
entity_envstringEnv var name holding the entity (falls back to WANDB_ENTITY / DEFAULT_WANDB_ENTITY)
projectstringW&B project; copied to Resource attribute wandb.project
project_envstringEnv var name holding the project (falls back to WANDB_PROJECT / DEFAULT_WANDB_PROJECT)
base_urlstringW&B base URL. Cloud default is https://trace.wandb.ai; Dedicated Cloud / Self-Managed hosts such as https://acme.wandb.io get /traces/otel/v1/traces appended
endpointstringOverride; skips base_url construction. Also via OTEL_WEAVE_ENDPOINT / WANDB_OTLP_ENDPOINT

The plugin sets the wandb-api-key header automatically. Weave is traces-only by default; set metrics: true / logs: true only if W&B documents ingest for those signals or you are pointing at a compatible collector. wandb.entity and wandb.project may also be supplied via top-level resource_attributes; conflicting values fail startup.

elastic​

FieldTypeDescription
endpointstringRequired. Elastic Cloud managed OTLP URL (https://<deployment>.ingest.<region>.<csp>.elastic.cloud) or a self-hosted EDOT Collector's OTLP/HTTP base. Also via OTEL_ELASTIC_ENDPOINT. A base URL gets /v1/traces appended; /v1/metrics and /v1/logs are derived from it
api_keystringElastic API key (inline; discouraged). Sent as Authorization: ApiKey <key>; the header is omitted entirely when no key resolves (a local collector)
api_key_envstringEnv var name holding the key (falls back to OTEL_ELASTIC_API_KEY / ELASTIC_API_KEY)
datasetstringOptional; copied to the data_stream.dataset Resource attribute. Lowercase letters, digits, _ and ., no -, at most 100 characters. The collector appends .otel
namespacestringOptional; copied to the data_stream.namespace Resource attribute, same character rules
metrics_temporalitystringPreset delta for this type (Elasticsearch does not take cumulative histograms); cumulative overrides

An invalid dataset / namespace skips the entry at startup with a warning rather than failing the agent. Both attributes are set on the shared Resource, so every configured backend receives them; two elastic entries with different values conflict at startup. See Elastic.

openlit​

FieldTypeDescription
endpointstringRequired. The OpenLIT OTLP/HTTP receiver base: http://localhost:4338 for the bundled stack, http://<host>:4318 upstream (the UI port does not proxy OTLP on the 2.1.0 image). Also via OTEL_OPENLIT_ENDPOINT. A base URL gets /v1/traces appended; /v1/metrics and /v1/logs are derived from it
api_keystringOpenLIT API key (inline; discouraged). Sent as Authorization: Bearer <key> to scope ingest to an organisation, project and environment; the header is omitted when no key resolves
api_key_envstringEnv var name holding the key (falls back to OTEL_OPENLIT_API_KEY / OPENLIT_API_KEY)
metrics / logs / tracesboolPer-signal toggles; all three default on

See OpenLIT.

mlflow​

FieldTypeDescription
endpointstringRequired. MLflow tracking server (http://localhost:5001 for the bundled stack, http://<host>:5000 upstream). Also via OTEL_MLFLOW_ENDPOINT. A base URL gets /v1/traces appended
experiment_idstringExperiment that receives the traces, sent as the mandatory x-mlflow-experiment-id header. Default 0 (the built-in Default experiment)
experiment_id_envstringEnv var name holding the experiment id (falls back to OTEL_MLFLOW_EXPERIMENT_ID / MLFLOW_EXPERIMENT_ID)
workspacestringOptional; sent as X-MLFLOW-WORKSPACE
api_key / api_key_envstringOptional tracking token, sent as Authorization: Bearer <token> (falls back to OTEL_MLFLOW_API_KEY / MLFLOW_TRACKING_TOKEN)
metrics / logsboolDefault off: the server has no /v1/metrics or /v1/logs route (404)

See MLflow.

opik​

FieldTypeDescription
endpointstringOpik base URL: http://localhost:5173 for the bundled stack, or a full …/api/v1/private/otel/v1/traces URL. Default https://www.comet.com/opik (Comet cloud). Also via OTEL_OPIK_ENDPOINT or the SDK's OPIK_URL_OVERRIDE
api_key / api_key_envstringComet API key, sent as a bare Authorization: <key> header (Opik's scheme; falls back to OTEL_OPIK_API_KEY / OPIK_API_KEY). Not needed on a self-hosted deployment
workspacestringComet workspace → Comet-Workspace header (cloud; falls back to OPIK_WORKSPACE)
project / project_envstringOpik project → projectName header (falls back to OPIK_PROJECT_NAME; Opik uses "Default Project" when absent)
metrics / logsboolDefault off: only /traces exists under the OTLP prefix

See Comet Opik.

laminar​

FieldTypeDescription
endpointstringLaminar app-server base: http://localhost:8100 for the bundled stack (upstream publishes 8000; the frontend port 5667 does not ingest). Default https://api.lmnr.ai (Laminar cloud). Also via OTEL_LAMINAR_ENDPOINT / LMNR_BASE_URL
api_key / api_key_envstringRequired. Project API key, sent as Authorization: Bearer <key> (falls back to OTEL_LAMINAR_API_KEY / LMNR_PROJECT_API_KEY). The key names the project
metricsboolDefault off: /v1/metrics answers 200 and stores nothing. true only with a collector in front
logsboolDefault on (log records are stored)

See Laminar.

langwatch​

FieldTypeDescription
endpointstringLangWatch base: http://localhost:5560 for the bundled stack, or the /api/otel prefix, or a full per-signal URL. Default https://app.langwatch.ai. Also via OTEL_LANGWATCH_ENDPOINT / LANGWATCH_ENDPOINT
api_key / api_key_envstringRequired. Project API key (sk-lw-…), sent as Authorization: Bearer <key> (falls back to OTEL_LANGWATCH_API_KEY / LANGWATCH_API_KEY)
project / project_envstringOptional project id → X-Project-Id, needed only with a service key (falls back to LANGWATCH_PROJECT_ID)
metrics / logs / tracesboolAll three default on; the plugin derives /api/otel/v1/metrics and /api/otel/v1/logs from the traces URL

See LangWatch.

latitude​

FieldTypeDescription
endpointstringLatitude ingest base: http://localhost:3002 for the bundled stack. Default https://ingest.latitude.so (Latitude cloud). Also via OTEL_LATITUDE_ENDPOINT / LATITUDE_INGEST_URL. A base URL gets /v1/traces appended
api_key / api_key_envstringRequired. API key, sent as Authorization: Bearer <key> (falls back to OTEL_LATITUDE_API_KEY / LATITUDE_API_KEY)
project / project_envstringRequired. Project slug for the mandatory X-Latitude-Project header (falls back to LATITUDE_PROJECT); spans without a project are rejected
metrics / logsboolDefault off: the ingest service has only /v1/traces

See Latitude.

Env var interpolation in headers:​

Inside any headers: value — and any other string field of a backends: entry, such as api_key, dsn, password or endpoint — ${VAR_NAME} is replaced with the env var's value when the config is loaded:

headers:
Authorization: "Bearer ${OTEL_AUTH_TOKEN}"
x-honeycomb-team: ${HONEYCOMB_API_KEY}

Missing env vars result in a startup warning and the literal ${VAR} being sent (which will fail auth, but visibly so).

Precedence​

For every field, precedence (highest → lowest) is:

  1. HERMES_OTEL_* env var (if applicable — see Env var reference)
  2. config.yaml value
  3. Built-in default

Validation​

On startup the plugin validates the config and:

  • Logs a warning naming the key and keeps the default for a value that cannot be parsed as the field's type (yaml or HERMES_OTEL_* env var)
  • Logs a single warning and uses an empty config if the YAML fails to parse
  • Logs a warning and ignores the file if it exists but pyyaml is not installed in the Hermes venv
  • Logs a warning and skips a backends: entry it cannot resolve (missing endpoint, unknown type)

The plugin never crashes Hermes because of config — at worst it disables itself with a clear log line.