Skip to main content

Overview

Local mode enables you to use Observatory without creating an account or providing an API key. All telemetry data is processed locally and displayed in a real-time widget in your browser. This is perfect for:
  • Development and testing
  • Prototyping AI agents
  • Debugging without cloud dependencies
  • Working offline
Local mode requires Next.js and the @contextcompany/widget package to visualize data in your browser.

How It Works

When you enable local mode:
  1. WebSocket Server: Observatory starts a local WebSocket server on an available port (tries ports 8081, 3001, 3002, etc.)
  2. Local Exporter: The LocalSpanExporter collects OpenTelemetry spans and stores them in memory
  3. Widget Connection: The browser widget connects to the WebSocket server to receive real-time updates
  4. Data Store: All runs, steps, and tool calls are organized by trace ID in a local data store
Local mode uses a SimpleSpanProcessor for immediate processing rather than batching spans. This provides instant feedback during development.

Setup

1

Install packages

Install the required Observatory packages:
npm install @contextcompany/otel @contextcompany/widget
2

Configure instrumentation

Create or update your instrumentation.ts file in the root of your Next.js project:
instrumentation.ts
export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    const { registerOTelTCC } = await import("@contextcompany/otel/nextjs");
    registerOTelTCC({ local: true });
  }
}
3

Add widget to your app

Add the widget script to your layout file:
app/layout.tsx
import Script from "next/script";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        {process.env.NODE_ENV === "development" && (
          <Script src="https://unpkg.com/@contextcompany/widget/dist/auto.global.js" />
        )}
      </head>
      <body>{children}</body>
    </html>
  );
}
4

Enable instrumentation

Enable instrumentation in your next.config.ts:
next.config.ts
const nextConfig = {
  experimental: {
    instrumentationHook: true,
  },
};

export default nextConfig;
5

Start your app

Run your Next.js development server:
npm run dev
The widget will appear in the corner of your browser window.

Configuration Options

Register Options

The registerOTelTCC function accepts these options:
registerOTelTCC({
  local: true,              // Enable local mode
  debug: true,              // Enable debug logging
  apiKey: "your-api-key",   // Optional: also send to cloud
  baseProcessor: processor, // Optional: custom span processor
  config: { /* ... */ }     // Optional: @vercel/otel config
})
If you provide both local: true and an API key that starts with dev_, Observatory will send telemetry to both local mode and the development cloud environment. Production API keys are not allowed with local mode.

Widget Options

If you’re manually initializing the widget (not using the auto script):
import { initWidget } from "@contextcompany/widget";

initWidget({
  enabled: true,           // Enable/disable widget
  onMount: () => {         // Callback when widget mounts
    console.log("Widget ready");
  }
});

WebSocket Server

Observatory automatically starts a WebSocket server for local communication:
  • Port Selection: Tries preferred ports (8081, 3001, 3002, 3003, 3004, 3005, 8000, 8001, 8080)
  • Fallback: Uses an ephemeral port if all preferred ports are taken
  • Auto-Discovery: The widget automatically scans these ports to find the server

Debug Mode

Enable debug logging to see WebSocket connection details:
registerOTelTCC({ 
  local: true,
  debug: true 
})
You can also enable debug mode in the widget by adding a data attribute:
<Script 
  src="https://unpkg.com/@contextcompany/widget/dist/auto.global.js" 
  data-debug="true"
/>

Data Storage

Local mode stores data in memory using the LocalSpanExporter:

Data Structure

type DataStore = {
  [traceId: string]: {
    run: UIRun | null;
    steps: UIStep[];
    toolCalls: UIToolCall[];
  };
};

Data Lifecycle

  • Collection: Spans are collected as they complete
  • Organization: Organized by trace ID (one trace per agent run)
  • Persistence: Data persists in memory until server restart
  • Reset: Use tccLocalExporter.reset() to clear the data store
Data is only stored in memory and is lost when you restart your development server.

Anonymous Telemetry

Local mode collects anonymous usage metrics to help improve Observatory:
  • Agent run completion events
  • Step and tool call events
  • Widget interaction events
  • No personally identifiable information is collected
Events captured:
  • local_mode_start
  • agent_run_end (with status code and duration)
  • step_end (with status code and duration)
  • tool_call_end (with status code and duration)
  • widget_expand_event

Troubleshooting

Widget Not Appearing

  1. Check that instrumentation is enabled in next.config.ts
  2. Verify the script tag is in your layout
  3. Look for WebSocket errors in the browser console
  4. Enable debug mode to see connection attempts

Port Conflicts

If all preferred ports are taken:
# Check what's using the ports
lsof -i :8081
lsof -i :3001

# Kill conflicting processes or let Observatory use an ephemeral port

Connection Refused

Ensure your Next.js app is running in Node.js runtime:
export async function register() {
  // This check is critical
  if (process.env.NEXT_RUNTIME === "nodejs") {
    const { registerOTelTCC } = await import("@contextcompany/otel/nextjs");
    registerOTelTCC({ local: true });
  }
}

HMR Issues

Local mode uses a singleton pattern to prevent multiple exporters during Hot Module Replacement:
const g = globalThis as unknown as {
  __TCC_LOCAL_EXPORTER__?: LocalSpanExporter;
};

export const tccLocalExporter =
  g.__TCC_LOCAL_EXPORTER__ ??
  (g.__TCC_LOCAL_EXPORTER__ = new LocalSpanExporter());

Next Steps

Widget

Learn how to use the real-time visualization widget

OpenTelemetry

Understand the OpenTelemetry integration

Build docs developers (and LLMs) love