# How the Motel OpenTelemetry Tracing System Captures Spans in Magnitude

> Discover how Magnitude exports OpenTelemetry spans to Motel using an OTLP JSON layer. Learn about trace ingestion and REST API exposure for dashboard insights.

- Repository: [Magnitude/magnitude](https://github.com/magnitudedev/magnitude)
- Tags: internals
- Published: 2026-09-08

---

**Magnitude exports OpenTelemetry spans to the Motel collector via an OTLP JSON layer configured through environment variables, which ingests traces at `http://127.0.0.1:27686/v1/traces` and exposes them through a REST API for dashboard consumption.**

The [magnitudedev/magnitude](https://github.com/magnitudedev/magnitude) repository implements distributed tracing using **Motel**, a local OpenTelemetry Protocol (OTLP) collector. When enabled, the system captures detailed execution spans from Effect-TS runtimes and makes them available for debugging through the ACN dashboard.

## Enabling Tracing via Environment Configuration

Tracing activation begins with environment variable detection in the `client-common` package. The `readOtelEndpoint` function in [`packages/client-common/src/tracing.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/client-common/src/tracing.ts) checks for specific flags to determine whether and where to export spans.

Set `MAGNITUDE_OTEL=1` to enable the default local collector, or specify a custom endpoint using `MAGNITUDE_OTEL_ENDPOINT` or `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`:

```ts
// packages/client-common/src/tracing.ts
const readOtelEndpoint = () => {
  if (process.env.MAGNITUDE_OTEL) {
    return "http://127.0.0.1:27686"; // Default Motel URL
  }
  return process.env.MAGNITUDE_OTEL_ENDPOINT 
    || process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT 
    || null;
}

```

If no endpoint is configured, the tracing layer returns `Layer.empty`, effectively disabling span export without breaking the application.

## Constructing the OTLP Export Layer

When tracing is enabled, `makeTracingLayer` constructs an `Otlp.layerJson` that serializes spans to JSON and transmits them over HTTP. This layer is defined in [`packages/client-common/src/tracing.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/client-common/src/tracing.ts) and performs two critical functions:

- **Configures the exporter**: Points to the resolved endpoint (defaulting to `http://127.0.0.1:27686`) and sets the service name (e.g., `magnitude-cli`)
- **Replaces the console logger**: Substitutes the default Effect logger with a no-op implementation to ensure logs flow exclusively through the OTLP pipeline

```ts
// Layer construction from packages/client-common/src/tracing.ts
const makeTracingLayer = (otelEndpoint: string) => {
  return Otlp.layerJson({ 
    baseUrl: otelEndpoint,
    resource: { serviceName: "magnitude-cli" }
  }).pipe(
    Layer.merge(Logger.replace(Logger.defaultLogger, Logger.none))
  );
};

```

The resulting `TracingLayer` (the default export from the module) can be provided to any Effect runtime stack.

## Integrating Tracing into the Runtime

Components throughout the Magnitude ecosystem—including the CLI, desktop client, and ACN daemon—activate tracing by adding `TracingLayer` to their Effect program layers. This single integration point enables span capture across the entire application surface.

```ts
import { TracingLayer } from "@magnitudedev/client-common";

// Apply to any Effect runtime
const program = Effect.all([
  // ... application effects
]).pipe(
  Effect.provide(TracingLayer)  // Activates Motel export
);

```

Once provided, all `Effect.log*` calls, `Effect.logSpan` invocations, and custom `Tracer` API usage automatically generate spans that route through the configured OTLP exporter rather than stdout.

## Capturing and Ingesting Spans

The **Motel** collector operates as a local HTTP server bound to `http://127.0.0.1:27686`. It exposes the standard OTLP endpoint at `/v1/traces`, receiving JSON-encoded span batches from the Effect runtime. Motel stores these traces in memory, making them immediately queryable without persistent storage overhead.

Throughout the codebase—particularly in `packages/agent` and related modules—execution flows generate telemetry through Effect's logging utilities:

```ts
// Example span generation (conceptual)
Effect.gen(function* () {
  yield* Effect.log("Processing RPC request");
  yield* Effect.logSpan("rpc-handler", Effect.sleep("100 millis"));
});

```

These calls serialize into OTLP-compliant payloads and transmit to Motel's ingestion endpoint via HTTP POST requests.

## Querying Traces via the Dashboard

The ACN dashboard server consumes captured spans through Motel's REST API. The `listRpcTraces` function in [`packages/acn-dashboard/src/server.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/acn-dashboard/src/server.ts) queries `/api/traces/search` and filters results to isolate `RpcServer.` operations, converting raw telemetry into the internal `RpcTraceSummary` shape.

```ts
// packages/acn-dashboard/src/server.ts
const listRpcTraces = async () => {
  const response = await fetch(
    `${MOTEL_BASE_URL}/api/traces/search`
  );
  const traces = await response.json();
  return traces
    .filter(t => t.name.startsWith("RpcServer."))
    .map(toRpcTraceSummary);
};

```

This transformation extracts trace IDs, RPC method names, duration in milliseconds, and error counts, presenting actionable debugging data in the dashboard UI.

## Configuration Examples

To enable **Motel OpenTelemetry tracing** in your Magnitude deployment:

```bash

# Use the default local collector

export MAGNITUDE_OTEL=1

# Or specify a remote OTLP endpoint

export MAGNITUDE_OTEL_ENDPOINT=http://otel.my-company.com:4318

```

Then provide the layer in your application entry point:

```ts
import { TracingLayer } from "@magnitudedev/client-common";

const runtime = Effect.runPromise(
  Effect.all([
    // ... your effects
  ]).provide(TracingLayer)
);

```

To fetch traces programmatically from the dashboard server:

```ts
import { listRpcTraces } from "@magnitudedev/acn-dashboard";

async function showRecentRpcTraces() {
  const traces = await listRpcTraces();
  console.table(traces.map(t => ({
    id: t.traceId,
    rpc: t.rpcName,
    durationMs: t.durationMs,
    errors: t.errorCount,
  })));
}

```

## Key Source Files

- [`packages/client-common/src/tracing.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/client-common/src/tracing.ts) – Defines `readOtelEndpoint`, `makeTracingLayer`, and the default `TracingLayer` export
- [`packages/acn/src/tracing.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/acn/src/tracing.ts) – ACN daemon-specific tracing configuration (mirrors client-common implementation)
- [`packages/acn-dashboard/src/server.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/acn-dashboard/src/server.ts) – Implements `listRpcTraces` and `toRpcTraceSummary` for dashboard queries
- `packages/acn-protocol/src/boundary/` – RPC contract definitions annotated with tracing metadata
- `packages/agent/` – Runtime span generation via `Effect.log*` and custom tracer calls

## Summary

- **Environment-driven configuration**: The `readOtelEndpoint` function activates tracing when `MAGNITUDE_OTEL=1` is set, defaulting to the local Motel instance at `http://127.0.0.1:27686`.
- **OTLP layer construction**: `makeTracingLayer` creates an `Otlp.layerJson` that serializes spans and suppresses console output, ensuring clean telemetry export.
- **Runtime integration**: Applications provide `TracingLayer` to their Effect stacks, enabling automatic span capture across CLI, desktop, and ACN components.
- **Motel ingestion**: The collector receives spans at `/v1/traces`, storing them in memory for immediate querying via the dashboard's `/api/traces/search` endpoint.
- **Dashboard visualization**: The ACN server filters RPC-specific traces and converts them into `RpcTraceSummary` objects for debugging.

## Frequently Asked Questions

### How do I configure a custom OpenTelemetry endpoint instead of Motel?

Set the `MAGNITUDE_OTEL_ENDPOINT` or `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` environment variable to your OTLP-compatible collector URL. The `readOtelEndpoint` function in [`packages/client-common/src/tracing.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/client-common/src/tracing.ts) prioritizes these over the default Motel URL when `MAGNITUDE_OTEL` is not explicitly enabled.

### What happens if tracing is not enabled?

When no endpoint is configured, `makeTracingLayer` returns `Layer.empty`, which passes spans through without exporting them. The application continues to function normally, with telemetry simply not transmitted to Motel.

### Does Motel persist traces to disk?

No. According to the implementation, Motel keeps trace data in memory only. It runs as a local HTTP server (typically at `http://127.0.0.1:27686`) and provides a transient store for development and debugging sessions rather than long-term retention.

### Where are RPC-specific spans filtered in the codebase?

The `listRpcTraces` function in [`packages/acn-dashboard/src/server.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/acn-dashboard/src/server.ts) queries Motel's `/api/traces/search` endpoint and filters for spans where the operation name starts with `RpcServer.`. It then applies `toRpcTraceSummary` to transform the raw OpenTelemetry data into a UI-friendly format containing trace IDs, method names, durations, and error counts.