Capture any OpenTelemetry-instrumented AI agent with SteelSpine. Zero code changes. 50+ frameworks supported via standard OTLP/HTTP.
OpenTelemetry is a vendor-neutral observability standard maintained by the Cloud Native Computing Foundation. As of 2026, every major AI agent framework supports it natively or via auto-instrumentation:
Because they all speak the same protocol, integrating any of them with SteelSpine is the same three-step process. Once the receiver is running, pointing a new framework at it is one environment variable.
steelspine otel-receiver --port 4318 --project <your-agent-name>
The receiver listens on port 4318 (the standard OTLP/HTTP port) and accepts traces over OTLP/HTTP with JSON encoding. It runs in the foreground; for production deployments, configure as a systemd service.
Useful flags:
| Flag | Purpose |
|---|---|
--port N | Listen port (default 4318) |
--run-id ID | Tag all events with a specific run ID |
--project NAME | Route events to a project namespace (workspace isolation) |
--verbose | Print each span as it arrives (useful for verifying capture) |
--once | Exit after one batch (useful for testing) |
Set these two environment variables in the shell where your agent runs:
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
The http/json protocol setting is required. SteelSpine's receiver currently accepts OTLP/HTTP with JSON encoding. Without setting the protocol to http/json, frameworks may default to protobuf which is not yet supported.
No code changes. Every span emitted by your agent gets captured into SteelSpine's signed event chain.
# LangChain agent
python3 my_langchain_agent.py
# CrewAI workflow
python3 my_crew.py
# Custom Python agent with OpenTelemetry SDK
python3 my_custom_agent.py
curl -s http://localhost:4318/health
Returns:
{
"status": "ok",
"run_id": "otel_20260524_143000",
"events_file": "/home/user/.prime/projects/my-agent/runs/otel_20260524_143000/events.jsonl",
"events_received": 47
}
steelspine otel-receiver --port 4318 --verbose
Each captured span prints to stdout as it arrives:
+1 event(s) from 1 span(s) 14:30:15
+1 event(s) from 1 span(s) 14:30:16
+1 event(s) from 1 span(s) 14:30:17
steelspine run list
steelspine verify-run --compliance-html > agent_audit.html
Opens in any browser. Shows event timeline, integrity status, regulatory tag block.
LangChain auto-instruments when OpenTelemetry environment variables are set:
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap --action=install
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
export OTEL_SERVICE_NAME=my-langchain-agent
opentelemetry-instrument python3 my_langchain_agent.py
Or use SteelSpine's native LangChain handler (no auto-instrumentation required):
pip install steelspine-langchain
# Then in your code:
from steelspine_langchain import SteelSpineCallbackHandler
handler = SteelSpineCallbackHandler()
llm = ChatOpenAI(callbacks=[handler])
pip install llama-index-instrumentation-opentelemetry
# In your code:
from llama_index.core import set_global_handler
set_global_handler("opentelemetry")
# Then run with env vars set as above
pip install opentelemetry-instrumentation-langchain
opentelemetry-bootstrap --action=install
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
opentelemetry-instrument python3 my_crew.py
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
# In your code:
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
)
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("my_operation"):
# your agent code
pass
npm install @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http
# Set env vars
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/json
node --require @opentelemetry/auto-instrumentations-node/register my_agent.js
SteelSpine's receiver converts each OTel span into one or more SteelSpine events:
| OTel concept | SteelSpine event field |
|---|---|
| Span name | line |
| Span status code OK (1) | kind: success |
| Span status code ERROR (2) | kind: failure with error message appended to line |
| Span status code UNSET (0) | kind: continuation |
| Instrumentation scope name | label |
| Span events (sub-events) | Each emitted as a child SteelSpine event |
| trace_id, span_id | Preserved as otel_trace and otel_span metadata |
OTEL_EXPORTER_OTLP_PROTOCOL=http/json is set. Without it, frameworks may default to protobuf and the receiver returns HTTP 400.OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 matches the receiver's port.--verbose to see incoming span batches.curl -X POST http://localhost:4318/v1/traces -H 'Content-Type: application/json' -d '{"resourceSpans":[]}' should return {"partialSuccess":{}}.steelspine otel-receiver --port 4318.curl http://localhost:4318/health.OTEL_EXPORTER_OTLP_PROTOCOL=http/json.OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_REQUEST=* or similar.cat ~/.prime/projects/<project>/runs/<run_id>/events.jsonl.Once events are in SteelSpine, the full pipeline applies:
steelspine compare run_A run_B: find divergence between two captured runssteelspine replay-run <run_id>: deterministic offline replaysteelspine verify-run --compliance-html: tamper-evident audit report for regulatorssteelspine pack-create <event_index>: portable signed audit packet for external verificationsteelspine eval --fail-on-diff: gate CI on agent regression