O
OpticsOps

How OpticsOps works

One npm package + two env vars. No manual spans. No framework plugins. No collector to deploy. Every HTTP hop is traced automatically.

Quick start

1Install the agent
npm install @opticsops/agent
2Set two env vars
OTEL_SERVICE_NAME=orders-api
OPTICSOPS_API_KEY=oo_your_key_here
OTEL_EXPORTER_OTLP_ENDPOINT=https://opticsops-ingest-551302578563.us-central1.run.app
3Preload the agent (no code change needed)
# ESM apps
node --import @opticsops/agent/register app.js

# CommonJS apps
node --require @opticsops/agent/register app.js

# Or via NODE_OPTIONS (Docker / Kubernetes)
NODE_OPTIONS="--import @opticsops/agent/register" node app.js
4Open your service map
https://opticsops-ingest.../dashboard#apiKey=oo_your_key

What gets traced

Traffic typeWhat happensStorage cost
Error (status ≥ 500)Full linked trace exported via OTLP1 row per span
Slow (duration > 500 ms)Full linked trace exported via OTLP1 row per span
Healthy (fast + 2xx)Counted in heartbeat aggregation1 row per 10 s window

This is tail-based sampling. The sampling decision is made after the request completes — never before — so you always capture the full chain for any request that actually failed.

What the agent collects

CollectedNot collected
HTTP method (GET, POST…)Request body
URL path + hostResponse body
HTTP status codeAuthorization / Cookie headers
Duration (ms)Query string values (only key names)
W3C traceparent for linkingDatabase queries or file contents
Service nameUser PII (unless in the URL path)

The pipeline

1

Agent (in your process)

Monkey-patches Node.js http/https. Reads W3C traceparent headers on inbound requests, injects them on outbound calls. Zero framework dependency.

2

Tail sampler

Buffers spans per trace. When the root span closes: if any span has status ≥ 500 or duration > 500 ms, the full trace is exported. Otherwise it is dropped.

3

Heartbeat aggregator

Healthy, fast requests are never exported as spans. The agent counts source → destination call volume and flushes aggregate counts every 10 seconds.

4

Ingest API (Cloud Run)

Receives OTLP spans (errors/slow) and heartbeat edge counts. Authenticates via X-Api-Key header. Writes to ClickHouse.

5

ClickHouse (GCE VM)

Stores spans, heartbeat_edges, and a materialised view (service_edges_mv) that pre-aggregates edge call counts per minute.

6

Dashboard (SSE + Cytoscape.js)

Queries the last 60 minutes of edges on load. Then opens a Server-Sent Events stream that refreshes every 5 seconds. Edges are coloured green / amber / red by error rate.

Honest limitations

  • Node.js only.The agent patches Node's built-in http / https modules. Python, Go, Java — not supported yet.
  • Tail sampling misses silent errors. If a service crashes before the root span closes (process kill, OOM), that trace is never exported.
  • No DB query tracing yet. The service map shows HTTP edges only. PostgreSQL, Redis, MongoDB calls are not traced (planned).
  • No log correlation. Logs and traces are separate — you cannot click a trace and see the associated log lines (planned).
  • Single ClickHouse VM. One GCE VM is a single point of failure. No replication, no automatic backup.
  • Early beta. APIs and the dashboard may change between releases without a migration path.

Want the full architecture — the datastore, the isolation model, how keys and sessions are handled? Read the architecture docs →