kavachOS

Telemetry

Exporting KavachOS authorization events as OpenTelemetry spans.

How it works

KavachOS converts authorization, delegation, and agent lifecycle events into OpenTelemetry-compatible span shapes and delivers them through a callback you provide. No @opentelemetry packages are required on KavachOS's end. You wire the spans into whichever OTel SDK or vendor you are already using.

No @opentelemetry dependency on KavachOS's side. The integration is a plain callback, so you can route spans to Datadog, Grafana Tempo, Honeycomb, or any other backend without any coupling.

Configuration

Pass a telemetry object to createKavach:

Prop

Type

Span shape

Each emitted span has this structure:

interface TelemetrySpan {
  traceId: string;          // 32-char hex
  spanId: string;           // 16-char hex
  name: string;             // e.g. 'kavach.authorize'
  kind: 'internal';
  startTime: string;        // ISO 8601
  endTime: string;          // ISO 8601
  attributes: Record<string, string | number | boolean>;
  status: 'ok' | 'error';
}

Span names

Span nameWhen it fires
kavach.authorizeEvery authorize() or authorizeByToken() call
kavach.delegation.createA delegation chain is created
kavach.delegation.revokeA delegation chain is revoked
kavach.agent.createAn agent is created
kavach.agent.revokeAn agent is revoked
kavach.agent.rotateAn agent token is rotated

Span attributes

AttributeTypeDescription
service.namestringFrom your serviceName config
kavach.agent.idstringAgent ID
kavach.actionstringThe requested action
kavach.resourcestringThe requested resource
kavach.resultstring'allowed' or 'denied'
kavach.duration_msnumberHow long authorization took
kavach.tokens_costnumberToken cost if provided
kavach.user.idstringOwner user ID if present
kavach.reasonstringDenial reason if denied
kavach.argumentsstringJSON-encoded arguments (only when includeArguments: true)

Delegation spans add kavach.delegation.id, kavach.delegation.from_agent, kavach.delegation.to_agent, kavach.delegation.depth, and kavach.delegation.expires_at.

Connecting to an OTel SDK

This example uses the @opentelemetry/sdk-node tracer directly:

import { createKavach } from 'kavachos';
import { trace } from '@opentelemetry/api';

const tracer = trace.getTracer('kavachos');

const kavach = await createKavach({
  database: { provider: 'postgres', url: process.env.DATABASE_URL! },
  telemetry: {
    serviceName: 'my-agent-service',
    onSpan(span) {
      const otelSpan = tracer.startSpan(span.name, {
        startTime: new Date(span.startTime),
        kind: 0, // SpanKind.INTERNAL
      });

      for (const [key, value] of Object.entries(span.attributes)) {
        otelSpan.setAttribute(key, value);
      }

      otelSpan.setStatus({ code: span.status === 'ok' ? 1 : 2 });
      otelSpan.end(new Date(span.endTime));
    },
  },
});

Connecting to Datadog

Datadog's APM SDK provides a tracer.startSpan API. Map KavachOS spans the same way:

import tracer from 'dd-trace';

const kavach = await createKavach({
  database: { provider: 'postgres', url: process.env.DATABASE_URL! },
  telemetry: {
    serviceName: 'my-agent-service',
    onSpan(span) {
      const s = tracer.startSpan(span.name, {
        startTime: new Date(span.startTime).getTime(),
      });

      for (const [key, value] of Object.entries(span.attributes)) {
        s.setTag(key, value);
      }

      s.finish(new Date(span.endTime).getTime());
    },
  },
});

Connecting to Grafana Tempo via OTLP

If you export via OTLP/HTTP using the @opentelemetry/exporter-trace-otlp-http package, the setup is the same as the OTel SDK example above. Point your OTLPTraceExporter at your Tempo endpoint and KavachOS spans will appear alongside your other application traces.

import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { NodeTracerProvider } from '@opentelemetry/sdk-node';
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';

const exporter = new OTLPTraceExporter({
  url: 'https://tempo.yourapp.com/v1/traces',
});

const provider = new NodeTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
provider.register();

// Now pass the tracer to kavach as shown above

Next steps

On this page