# Voyage observability — local Docker Compose stack

A version-pinned local-development stack for inspecting Voyage v4.1 metrics
emitted by `hooks/scripts/otel-export.mjs`. Two complementary paths:

| Mode | env var | Pull/push | Container that scrapes |
|------|---------|-----------|------------------------|
| Prometheus textfile | `VOYAGE_EXPORT_MODE=textfile` | voyage writes `voyage.prom` to `./voyage-textfile/`, node-exporter scrapes | `node-exporter` |
| OTLP/HTTP | `VOYAGE_EXPORT_MODE=otlp` | voyage POSTs to `http://localhost:4318/v1/metrics` | `otel-collector` (re-exposed as Prometheus on `:8889`) |

Both modes feed Prometheus → Grafana.

## Quickstart

```bash
cd examples/observability
mkdir -p voyage-textfile
docker compose up -d
```

Endpoints (all bound to `localhost`):

| Service | URL |
|---------|-----|
| Prometheus UI | http://localhost:9090 |
| Grafana UI | http://localhost:3000 (anonymous Viewer enabled; admin/admin) |
| OTLP/HTTP receiver | http://localhost:4318/v1/metrics |
| Node Exporter | http://localhost:9100/metrics |
| OTel Collector Prometheus exporter | http://localhost:8889/metrics |

Stop with `docker compose down`. Add `-v` to wipe Prometheus + Grafana volumes.

## Activating Voyage export

In another terminal, set one of the env vars before invoking a Voyage command:

```bash
# Path A — textfile mode
export VOYAGE_EXPORT_MODE=textfile
export VOYAGE_TEXTFILE_DIR="$(pwd)/voyage-textfile"

# Path B — OTLP mode
export VOYAGE_EXPORT_MODE=otlp
export VOYAGE_OTEL_ENDPOINT=http://localhost:4318/v1/metrics
export VOYAGE_OTEL_ALLOW_PRIVATE=1   # required: localhost is loopback (RFC1122)
```

The Stop hook (wired in `hooks/hooks.json`) will run
`hooks/scripts/otel-export.mjs` automatically at session-end.

See `docs/observability.md` for the full env-var matrix and security notes.

## Pinned versions (per research/01)

| Component | Image | Pinned to |
|-----------|-------|-----------|
| Prometheus | `prom/prometheus` | `v3.0.1` |
| Node Exporter | `prom/node-exporter` | `v1.10.2` |
| Grafana | `grafana/grafana` | `11.4.0` |
| OTel Collector (contrib) | `otel/opentelemetry-collector-contrib` | `0.115.0` |

These are reference versions for the v4.1 release window; bump only after
re-testing the full smoke flow.

## Limitations

- Stop-hook export runs at *normal* session end. If Claude Code exits via
  crash or hard kill, the final metrics for that session are not flushed.
  Use `--resume` on next start to recover plan/progress state; metrics for
  the unflushed session will be missing from Prometheus.
- The `VOYAGE_OTEL_ALLOW_PRIVATE=1` escape hatch enables sending to
  RFC1918 addresses (home-lab use). It is **off by default** so accidental
  internal-network exfiltration is blocked. See `docs/observability.md`.
- This stack is for *local development*. Do not expose ports `:9090` /
  `:3000` / `:4318` outside loopback — Grafana ships with anonymous viewer
  access enabled and admin/admin credentials.
