Tutorial: Third-party OTEL trace to offline openssl proof
Documented for Akmon 2.2.0.
Time estimate: 15-20 minutes
Complexity: Intermediate
Who this is for
Teams whose agents are already instrumented with a third-party OpenTelemetry GenAI
instrumentation, who want a signed, standalone-verifiable audit record of a session, and
auditors who must check that record with stock openssl alone: no Akmon binary, no cloud,
no vendor lock-in.
This is the headline trust loop, end to end, on what real agents emit today: a real-framework OTLP trace becomes an AGEF bundle that a counterparty verifies offline. It is producer-agnostic; it does not require Akmon's own agent at all. It is the concrete answer to the gaps competitors leave: HMAC-only or unsigned manifests, no standalone verifier, cloud-locked verification, and "cannot replay."
What you will have at the end
- An AGEF bundle built from a third-party OpenTelemetry GenAI trace.
- An Ed25519 signature over the session head, verifiable by anyone who trusts the public key.
- Three artifacts (
statement.bin,signature.bin,pubkey.pem) that a third party verifies with OpenSSL 3.x and nothing else.
The fixture
This walkthrough uses the checked-in fixture
crates/akmon-cli/tests/fixtures/openai_v2_weather_legacy.otlp.json. It is a representative,
illustrative OTLP/JSON trace that models the default emission of the
opentelemetry-instrumentation-openai-v2 Python instrumentation, in the legacy (<= v1.36)
message-event form (gen_ai.system.message / gen_ai.user.message / gen_ai.choice span
events). It is hand-authored to match that documented shape, contains no real user data or
PII, and, because that instrumentation does not capture message content unless
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT is enabled (default off), carries no
message bodies. See crates/akmon-cli/tests/fixtures/README.md for the full provenance note.
Prerequisites
akmoninstalled and onPATH.- OpenSSL 3.x for the final verification step. Stock LibreSSL (the macOS
/usr/bin/openssl) cannot verify Ed25519: it lacks-rawinand cannot load Ed25519 keys.
Steps
- Import the third-party OTEL trace into a fresh AGEF session.
akmon otel import crates/akmon-cli/tests/fixtures/openai_v2_weather_legacy.otlp.json \
--journal ./journal --format json
The JSON report records capture_level, the provider/tool counts, and the new session_id:
{
"capture_level": "structural",
"provider_calls": 1,
"tool_calls": 1,
"turns_emitted": 0,
"turns_suppressed_no_content": 1,
"semconv_version": "1.37.0",
"session_id": "de52d29b-e7ee-4f53-8526-3c479d4f8c37"
}
- Export the session as an AGEF bundle.
akmon bundle export <session-id> --journal ./journal --output ./audit.akmon
- Sign the bundle's session head with an Ed25519 key and publish the public key (hex).
akmon bundle sign ./audit.akmon --key signer.pk8 --format json
- Verify integrity and the signature, requiring a signature to be present.
akmon bundle verify ./audit.akmon --verify-key signer.pub.hex --require-signature --format json
- Emit the standalone verification artifacts.
akmon bundle prove-openssl ./audit.akmon --verify-key signer.pub.hex --out-dir ./proof
- Verify offline with OpenSSL 3.x alone (this command is also printed by the step above).
openssl pkeyutl -verify -pubin -inkey ./proof/pubkey.pem -rawin -in ./proof/statement.bin -sigfile ./proof/signature.bin
A valid signature prints Signature Verified Successfully and exits 0. Tampering with
statement.bin (or using the wrong signature) makes openssl print Signature Verification Failure and exit non-zero.
Optional: bind an operator identity
The head signature proves the bundle's integrity is authentic, but says nothing about who
operated the session. To attach a named operator (and have it verify offline too), generate an
operator key, attest, and pass --operator-key to verify and to prove-openssl.
akmon bundle keygen --out operator.pk8 --public-out operator.pub.hex
akmon bundle attest ./audit.akmon --key operator.pk8 --operator-id ops@example.com --role approver
akmon bundle verify ./audit.akmon --verify-key signer.pub.hex --require-signature \
--operator-key operator.pub.hex --require-operator --format json
akmon bundle prove-openssl ./audit.akmon --verify-key signer.pub.hex \
--operator-key operator.pub.hex --out-dir ./proof
The last step emits three more files alongside the head-signature artifacts:
operator_statement.bin, operator_signature.bin, operator_pubkey.pem, and a third party
verifies the operator attestation with OpenSSL 3.x alone:
openssl pkeyutl -verify -pubin -inkey ./proof/operator_pubkey.pem -rawin -in ./proof/operator_statement.bin -sigfile ./proof/operator_signature.bin
Trust the key, not the name. Verification proves only that the holder of operator.pub.hex
signed the operator_id/role claims. It does not prove the person is who the name says. A
verifier decides which operator key they trust out-of-band (a directory, a roster, a key
ceremony); only then does the self-asserted name carry weight. See
akmon bundle attest.
Honesty: this is STRUCTURAL capture, not full replay
The source instrumentation did not capture message content (the content-off default), so Akmon
imports the trace as capture_level=structural: metadata only. This is surfaced, not hidden:
- The import report and
akmon bundle verify --format jsonboth report the level asstructural(under/capture/level). - The full-capture gate correctly fails on this bundle:
akmon bundle verify ./audit.akmon --require-capture full
This exits 1: a metadata-only OTEL import must never read as VERIFIED-full. The integrity and
signature still verify (the evidence is intact and authentic); what is absent is the
verbatim message content, so no byte-level or full replay is implied from imported telemetry.
A note on versions: the fixture's source form is the legacy <= v1.36 message-event
convention, while Akmon records source_semconv 1.37.0 in the signed session config for all
imports regardless of the source form (a hardcoded constant). The recorded value is therefore not
a faithful descriptor of the source form; this is documented in the fixture README and is cosmetic.
How a reviewer validates this
- Confirm
akmon otel importexits0and reportscapture_level=structural. - Confirm
akmon bundle verify --verify-key --require-signatureexits0with averifiedsignature outcome and/capture/levelequal tostructural. - Confirm
akmon bundle verify --require-capture fullexits1. - Confirm the OpenSSL 3.x command exits
0for the emitted artifacts and non-zero whenstatement.binis tampered.
Verified by an automated test
This entire chain (import, export, sign, verify, --require-capture full failure,
prove-openssl, and the real openssl positive/tamper-negative legs) is asserted as ONE flow by
t_e2e_otel_legacy_trace_to_openssl_proof in
crates/akmon-cli/tests/e2e_otel_to_openssl_integration.rs, against the same fixture. A companion
test, t_e2e_otel_proof_artifacts_byte_identical, locks the emitted artifacts byte-for-byte
without requiring openssl, so the proof holds even where the openssl leg skips. Doc and test cannot
drift.
Troubleshooting
- If the openssl step reports
unable to loador usage text, you are likely on LibreSSL. Use an OpenSSL 3.x build (the verification command needs-rawinand Ed25519 support). - If
akmon bundle signrejects the key, generate a PKCS#8 v2 Ed25519 key; some tools emit PKCS#8 v1, which the signing path rejects.