obs-unified

Getting started

Start the unified collector and get multiple signal types flowing into one dashboard.

This guide gets you from a fresh checkout to unified observability in the dashboard. The goal is not just to send a trace or a log; it is to see multiple signal types connected through the same collector and identity chain. There are two decisions:

  1. How do you want to run obs-unified? Use the all-in-one Docker image, or install and run the repo locally.
  2. What data do you want to look at? Use seeded sample data, the Astronomy Shop demo, or telemetry from your own app.

Choose how to run

Runtime pathUse whenStarts
Option 1 — Docker imageYou want the quickest first run with the fewest host dependencies.Postgres, collector, dashboard, and filesystem blob store in one container: pull the prebuilt image from GHCR, or build the local image to include sample data.
Option 2 — local installYou want to edit code, run dev servers, or inspect internals while using the repo.Local collector, demo API, and Vite dashboard from the workspace.

Choose what to observe

Data pathUse whenStart here
Seeded sample dataYou want populated dashboards immediately.Built into Option 1, or run pnpm run seed with Option 2.
Astronomy Shop demoYou want realistic microservice traffic and service-map edges.Run the OpenTelemetry demo against your local collector.
Your own appYou want to validate obs-unified against a real application.Add SDKs or OpenTelemetry exporters pointing at your collector.

Prerequisites

  • Docker: required for the Docker image and Astronomy Shop demo.
  • Node.js 22+ and pnpm 10+: required for local install and repo scripts.

Option 1 — Docker image

This packages the full local stack into one container. It is the lowest-friction first run.

Prebuilt image from GHCR

No clone needed. The release image is published as ghcr.io/dev-sawan-ruparel/obs-unified and fails closed without your own credentials:

export INGEST_KEY="pick-a-long-random-ingest-key"
export DASHBOARD_PASSWORD="pick-a-dashboard-password"
export DASHBOARD_TOKEN="pick-a-read-token-for-mcp-and-cli"

docker run --rm \
  -p 5173:5173 \
  -p 8790:8790 \
  -e INGEST_KEY \
  -e DASHBOARD_PASSWORD \
  -e DASHBOARD_TOKEN \
  -e OBS_LOCAL_SEED=true \
  -v obs-unified-db:/var/lib/postgresql \
  -v obs-unified-blobs:/data \
  ghcr.io/dev-sawan-ruparel/obs-unified:latest

Dashboard at http://localhost:5173, collector at http://localhost:8790. Passing -e OBS_LOCAL_SEED=true populates all demo scenarios (traces, logs, AI calls, Agent Action Graph, and AutoSRE Scenario D) immediately on first boot; omit it when you want an empty collector for Astronomy Shop or your own app.

Build the local image with sample data

Build and run from the personal monorepo:

git clone https://github.com/dev-sawan-ruparel/obs-unified.git
cd obs-unified
pnpm install --frozen-lockfile
pnpm local:image
pnpm local:run

Or use Docker directly:

docker build -f Dockerfile.local -t obs-unified/local:dev .
docker run --rm -p 5173:5173 -p 8790:8790 obs-unified/local:dev

Access the interfaces:

InterfaceURL
Dashboardhttp://localhost:5173
Collector ingesthttp://localhost:8790

To log in from a script, POST the dashboard password and keep the obs_session cookie. Logout only clears the browser cookie; the signed session stays valid until it expires (7 days) or DASHBOARD_PASSWORD is rotated.

curl -i -X POST http://localhost:5173/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"password":"<DASHBOARD_PASSWORD>"}'
# set-cookie: obs_session=...; HttpOnly; SameSite=Strict; Max-Age=604800

Credentials for the locally built image (pnpm local:run only; the prebuilt GHCR image rejects these values and requires your own, see Installation):

CredentialEnvironment VariableLocal image value
Ingest write keyINGEST_KEY / OBS_INGEST_KEYdev-ingest-key
Dashboard passwordDASHBOARD_PASSWORDe2e-test-pass
Read bearer tokenDASHBOARD_TOKEN on the collectore2e-dashboard-token

Tokens and env vars

WhereVariablePurpose
CollectorINGEST_KEYWrite-only key that SDKs and exporters present to ingest telemetry.
CollectorDASHBOARD_PASSWORDBrowser login for the dashboard (POST /auth/login).
CollectorDASHBOARD_TOKENRead bearer token for /internal/*, the CLI, and MCP.
Client (CLI, MCP)OBS_DASHBOARD_TOKENCanonical name for the value of the collector's DASHBOARD_TOKEN. Accepted by the CLI (doctor, keys, changes list), and @obsunified/mcp-server.
Client (CLI)OBS_ADMIN_TOKEN, OBS_API_TOKENAliases for OBS_DASHBOARD_TOKEN (verified against CLI 1.1.3 with doctor, keys list, and changes list).
Client (SDKs, CLI)OBS_INGEST_KEYThe collector's INGEST_KEY, used by the SDKs and obs-unified changes record (OBS_API_KEY is also accepted by the CLI).

To persist local database and blob storage state across container restarts:

docker volume create obs-unified-local-db
docker volume create obs-unified-local-blobs
docker run --rm \
  -p 5173:5173 \
  -p 8790:8790 \
  -v obs-unified-local-db:/var/lib/postgresql \
  -v obs-unified-local-blobs:/data \
  obs-unified/local:dev

Verify the full first-run path:

pnpm smoke:local-image

The smoke test builds the image, boots a fresh container, seeds data, and verifies collector health, dashboard HTML, and login from outside Docker.

Option 2 — local install

Use this when you are modifying dashboard code, collector code, or SDK packages.

git clone https://github.com/dev-sawan-ruparel/obs-unified.git
cd obs-unified
pnpm install
pnpm run setup
pnpm run dev

pnpm run setup creates apps/collector/.dev.vars from apps/collector/.dev.vars.example if needed, then runs local migrations. If you change the local dashboard password or ingest key later, pnpm run seed reads the same file automatically.

The local dev stack exposes:

ServiceURL
Dashboardhttp://localhost:5173
Demo APIhttp://localhost:8787
Collectorhttp://localhost:8790

Seeded sample data

Use this when you want populated dashboards immediately.

  • Docker image: sample data is seeded automatically on startup.
  • Local install: run the seeder in a second terminal.
pnpm run seed

Expected result: Traces, Logs, AI Calls, Usage, and Issues show sample data. Replays require browser interaction, so open the dashboard's Playground tab and trigger a replay-producing interaction once.

Astronomy Shop demo

Use this when you want realistic microservice traffic and service-map edges. It runs the official OpenTelemetry Astronomy Shop demo and points its exporters at your local obs-unified collector.

This path assumes the collector is already running. For local development, start it with:

pnpm dev:collector

Prepare the upstream demo services:

pnpm demo:setup
pnpm demo:preflight

Launch the microservices:

pnpm demo:up

Access URLs:

InterfaceURL
Dashboardhttp://localhost:5173
Shop web interfacehttp://localhost:8080

The load generator takes roughly 30 seconds to begin driving traffic. Once running, Traces, Service Maps, Issues, Logs, and Metrics populate dynamically from active microservice calls.

Stop and remove the compose stack:

pnpm demo:down

Your own app

This is a data path that sends telemetry to whichever collector you started above.

TargetSetup guide
React/Vite frontend + Hono APIReact + Hono walkthrough
Python Flask APIPython Flask walkthrough
Browser-only applicationAnalytics SDK reference
TypeScript backendTelemetry SDK reference
Polyglot recipesExamples

Application compatibility & signal matrix

Before instrumenting an application (or when an AI agent scouts your repository), use this matrix to verify which signals are supported for your architecture:

TierApplication shapeSupported signalsNotes & constraints
Tier 1 — Full First-Party Unified StackBrowser SPAs & SSR web apps (React, Next.js, Vite, Vue, Svelte, Vanilla TS/JS) + TypeScript/Node/Edge backends (Cloudflare Workers, Hono, Express, Fastify) or Go/Rust servicesTraces, Structured Logs, Metrics, Usage Events, rrweb DOM Session Replay, Click-to-Trace (x-obs-interaction), AI Calls, Agent Action Graph, CPU Profiles, AutoSRE Release Lineage & Feature FlagsFull out-of-the-box correlation via @obsunified/analytics-sdk, @obsunified/telemetry-sdk, @obsunified/sdk, sdks/go, and sdks/rust.
Tier 2 — Standard OpenTelemetry BackendsPython (Flask, FastAPI, Django), Java/Kotlin (Spring Boot), .NET, Ruby, PHP, C++OTLP HTTP Traces (/v1/traces), Logs (/v1/logs), Metrics (/v1/metrics), plus HTTP JSON ingest for AI Calls (/v1/ai), Agent Action Graph (/v1/actions, /v1/agent-runs, /v1/tool-calls), and Release Changes (/v1/changes)Supports both OTEL_EXPORTER_OTLP_PROTOCOL=http/json and http/protobuf (with optional gzip). For browser click-to-trace correlation, read the incoming x-obs-interaction HTTP header and set span attribute obs.interaction.id.
Tier 3 — Partial or Constrained ShapesNative Mobile (iOS, Android, React Native, Flutter)
Headless / CLI / Cron
Async Message Queues (Kafka, SQS, RabbitMQ, BullMQ)
gRPC-only OTLP
Backend traces, logs, metrics, AI calls, and Action Graph (startAgentRun({ agentId, agentName, ... }, fn))Native mobile: No rrweb session replay (requires browser DOM).
Queues: Producer must propagate traceparent / x-obs-interaction / x-obs-action-id in message headers or consumer spans appear as disconnected roots.
gRPC OTLP: Collector endpoints speak HTTP (application/json and application/x-protobuf); place an OTel Collector gateway in front if agents only emit gRPC.

Common wiring & non-interactive scaffolding

  1. Deploy or run an accessible collector endpoint.
  2. Scaffold a pre-wired template (supports non-interactive flags for CI and AI agents) or install the SDKs into an existing app:
pnpm dlx @obsunified/cli create my-app \
  --template react-vite \
  --collector-url http://localhost:8790 \
  --yes

Available --template (-t) values: react-vite, vanilla-ts, hono-workers.

  1. Configure OBS_COLLECTOR_URL and a write-only ingest key (INGEST_KEY).
  2. Ensure API CORS policies allow x-obs-interaction and your app's local dev origin (the Docker images allow localhost / 127.0.0.1 on ports 5173, 5174, and 3000 by default; add more with -e ALLOWED_ORIGINS="http://localhost:5173,http://localhost:4173", a comma-separated list that replaces the default).
  3. Verify connectivity and CORS headers:
pnpm dlx @obsunified/cli doctor http://localhost:8790 --origin http://localhost:3000

Autonomous AI agent setup & verification playbook

If you are an AI coding agent (Claude Code, Cursor, Codex, Antigravity, Windsurf) tasked with setting up Observability Unified for a user:

  • Machine-readable documentation: Fetch https://obsunified.com/llms.txt, https://docs.obsunified.com/llms.txt, or https://docs.obsunified.com/llms-full.txt, or install the prebuilt .skill bundles from the skills-v0.1.4 release.
  • Step 1 (Assess fit): Inspect the user's package.json, pyproject.toml, go.mod, or Cargo.toml against the Application compatibility & signal matrix above. Report clearly to the user which signals are supported and call out any Tier 3 constraints before editing code.
  • Step 2 (Boot collector & verify): Start the GHCR container with INGEST_KEY, DASHBOARD_PASSWORD, and DASHBOARD_TOKEN set, then run pnpm dlx @obsunified/cli doctor http://localhost:8790 --origin <user-app-origin>.
  • Step 3 (Instrument without blocking on TTY prompts): Always pass --yes (-y) and --template when running pnpm dlx @obsunified/cli create. When instrumenting Cloudflare Workers or Hono, register app.use("*", async (c, next) => { await next(); c.executionCtx.waitUntil(flushSpans()); }).
  • Step 4 (Verify ingestion end-to-end): Trigger a test request or record a release change (pnpm dlx @obsunified/cli changes record --url http://localhost:8790 --api-key $INGEST_KEY --service my-api --version 0.1.0 --environment <env> --summary "Agent onboarding verification"; --environment defaults to default, so pass the environment your instrumented service reports), then confirm non-zero telemetry with pnpm dlx @obsunified/cli changes list --url http://localhost:8790 --token $DASHBOARD_TOKEN and curl -fsSL -H "Authorization: Bearer $DASHBOARD_TOKEN" http://localhost:8790/internal/telemetry/overview.
  • Step 5 (Connect MCP server): Configure @obsunified/mcp-server with OBS_COLLECTOR_URL and OBS_DASHBOARD_TOKEN ($DASHBOARD_TOKEN); it only reads, so do not give it the ingest key — see MCP Server.

What is autonomous vs. what requires human intervention

PhaseWhat the AI agent does autonomouslyWhen human intervention is needed
1. Discovery & stack fitInspects repo manifests (package.json, pyproject.toml, go.mod, Cargo.toml) against the 3-Tier Compatibility Matrix.Only if the app has Tier 3 constraints (e.g., native mobile without browser DOM for rrweb replay, or unpropagated message queues) and needs scope confirmation.
2. Collector startupChecks docker info, boots ghcr.io/dev-sawan-ruparel/obs-unified:latest on 5173/8790 with credentials it generates, and runs pnpm dlx @obsunified/cli doctor.• If Docker Desktop is stopped or not installed on the machine.
• For production cloud deploys requiring interactive OAuth (wrangler login), DNS setup, or cloud billing/account selection.
3. Code instrumentation & envScaffolds with pnpm dlx @obsunified/cli create --yes or installs SDKs, wires middleware (flushSpans()), and populates .env.local / .dev.vars + .env.example.• Adding encrypted production secrets in remote CI/CD or hosting providers (Vercel, Netlify, GitHub Actions, Cloudflare Worker secrets).
• Confirming privacy consent before enabling continuous DOM recording (startReplay()) in production.
4. End-to-end verificationStarts the app server, fires synthetic HTTP requests, captures x-obs-trace-id, and queries GET /internal/telemetry/overview (Authorization: Bearer $DASHBOARD_TOKEN).Only if verifying visual rrweb DOM replay when the agent has no headless browser tool (human clicks once in a real browser tab).
5. MCP & AutoSRE operationsRegisters @obsunified/mcp-server, investigates traces/logs/blast radius, and proposes governed remediations.Approving pending_human_approval remediation proposals in the dashboard when causal proof is below cohort isolation or counterfactual verification.

Standalone Node collector variant

The all-in-one Docker image is the recommended first-run Docker path. If you only want the collector service backed by Postgres and RustFS/S3, use the standalone Node collector:

cd apps/collector-node
docker compose up -d
docker compose logs -f collector

On this page