# Lobe

**Local HTTP performance profiling for developers.**

See exactly where every request spends its time. DNS, TCP, TLS, TTFB, and download phases captured against grounded network baselines — so you know when something is *actually* slow versus just fine.

- Zero-config local proxy — no agent to install, no APM to configure
- Works with any language, framework, or mobile app you already run
- Free tier runs entirely on your machine — no account required
- Optional cloud sync for historical baselines and cross-session diffing

Learn more at [getlobe.dev](https://getlobe.dev).

## Install

Two paths, whichever fits your setup.

```bash
# macOS + Linux via Homebrew
brew install kpwithcode/lobe/lobe

# Any platform with the Rust toolchain (macOS, Linux, Windows, WSL, Alpine, …)
cargo install lobe-cli
```

Verify:

```bash
lobe --version
```

No account required to run any of the CLI commands below.

## Quickstart

Point Lobe at a local dev server and route traffic through it:

```bash
lobe capture http://localhost:3000
```

That spins up a local proxy on `http://127.0.0.1:7878`. Open that URL in your browser instead of `localhost:3000` — every request through it gets its DNS, TCP, TLS, TTFB, and download phases recorded.

Inside the TUI:

- `q` — quit
- `e` — export the current session to `/tmp/lobe-capture-session-*.json`
- `u` — upload session to your Lobe account (requires `lobe login` first)

## Two modes

Lobe has two capture flavors. Use whichever matches the question you're trying to answer.

### `lobe capture <upstream>`

Real request traffic routed through a local proxy. Best for answering *"why is this page slow when I click around?"* Every request the app makes flows through Lobe, and every phase is recorded.

```bash
lobe capture http://localhost:3000
```

### `lobe watch <url>`

Synthetic probing of a single URL on an interval. Best for answering *"is this endpoint drifting?"* or *"is my baseline still good after this deploy?"*

```bash
lobe watch https://api.example.com/health --tui
```

## Capturing a local web app

```bash
lobe capture http://localhost:5173 --listen 127.0.0.1:7878
```

Then browse through the proxy at `http://127.0.0.1:7878` instead of `localhost:5173` directly.

Two details worth knowing:

- **`localhost` vs `127.0.0.1` is not interchangeable.** Match whatever your app normally answers on — different hosts, different DNS paths.
- **Change the proxy port** with `--listen 127.0.0.1:9090` if `7878` is already taken.

## Capturing a mobile app

Put Lobe on your machine's LAN IP so your phone can reach it. The mobile app talks to the proxy; the proxy forwards upstream to your real backend on its normal port.

```bash
lobe capture http://192.168.1.169:8000 --listen 0.0.0.0:7878
```

Then point the mobile app at the proxy:

```ts
const DEV_API_URL = "http://192.168.1.169:7878";
```

**Do not** move your backend to `7878`. Keep it on `8000` (or wherever it already runs). The app calls `7878`, Lobe forwards to `8000`. Use your machine's LAN IP, not `localhost` — a physical phone can't resolve `localhost` back to your laptop.

For a desktop browser on the same machine, `127.0.0.1` is fine. For a phone, simulator, or device on the network, use the LAN IP.

## Exporting sessions

While a `capture` or `watch` session is running, press `e` to export it as JSON:

```text
/tmp/lobe-capture-session-YYYYMMDD-HHMMSS.json
```

Exports can be replayed offline, shared with a teammate, or fed into `lobe explain` for an AI-generated triage report (see below).

## Building from source

You don't need to build from source to use Lobe — just install via the methods above. If you're contributing or want to run against unreleased changes:

```bash
git clone https://github.com/kpwithcode/lobe.git
cd lobe
cargo run -p lobe-cli --bin lobe -- capture http://localhost:3000
```

## Web

The Lobe web app lives in `apps/lobe-web` and is built with Next.js plus React Flow.

It currently includes:

- project list
- session history
- session overview
- request-flow graph view
- cross-session compare view

Install and run it with:

```bash
cd apps/lobe-web
npm install
npm run dev
```

Then open:

```text
http://localhost:3000
```

### Load Real Capture Data In Web

The web app can ingest exported capture sessions in addition to the built-in mock data.

1. Export a capture session from the CLI with `e`
2. Copy the JSON into:

```text
apps/lobe-web/data/imported-capture-sessions.json
```

The file shape is:

```json
{
  "capture_sessions": [
    {}
  ]
}
```

The repository already includes an empty starter file at that path. Replace its contents with your exported session payload to have the web UI render real imported capture sessions alongside the mock examples.

## How Lobe measures (internal reference)

Reference for what the numbers on the dashboard actually mean. Kept in-tree so engine changes stay consistent with the analytics layer, and so marketing copy stays grounded in what's true. Not intended as user-facing docs.

### Phase decomposition

Every request is split into five phases:

- **DNS** — name resolution to the upstream host
- **TCP** — three-way transport handshake
- **TLS** — handshake for HTTPS (zero for plaintext HTTP)
- **TTFB** — request bytes sent → first response byte in (server-side time lives here)
- **Download** — first response byte → last response byte

Total is the sum plus a small proxy overhead (parsing, forwarding).

### Cold vs warm connections

First request to a host is *cold*: every phase runs.

Subsequent HTTP/2 requests to the same host reuse the pooled connection and are *warm*: DNS / TCP / TLS are effectively zero, only TTFB and download reflect real work. The `connection_reuse` field on `CapturedExchange` records `new` for cold, `reused` for warm.

`detectAnomaly` in `apps/lobe-web/lib/analytics.ts` uses this to pick which phases to judge. Cold: all five. Warm: TTFB, download, total only — so a 4ms multiplexed request doesn't trip an "impossibly fast" flag, and a 400ms cold TLS handshake isn't judged against a warm baseline.

Pool implementation: `crates/lobe-core/src/engine/pool.rs` — thread-safe host→`SendRequest` map, 30s idle timeout, cloned senders for multiplexing. HTTP/1.1 requests currently always show `new` (no keep-alive pool yet). Fine for local dev because loopback handshake cost is near zero. Post-launch hardening milestone if legacy production HTTPS/1.1 traffic becomes a real customer complaint.

### Napkin baselines

Grounded thresholds by network environment:

| category  | TTFB     | total     |
|-----------|----------|-----------|
| loopback  | ≤ 50ms   | ≤ 80ms    |
| LAN       | ≤ 100ms  | ≤ 200ms   |
| remote    | ≤ 500ms  | ≤ 1.5s    |

These are the physical limits of the network — anything slower has a cause worth finding. Anomaly copy points to the phase that overshot and phrases the delta in human terms.

### Bimodal detection

Route-scoped percentiles reveal bimodal patterns (cache hit vs miss, fast path vs slow path) at ≥30 samples per route. Below 30, distributional claims are noise. Above 30, the second mode is surfaced explicitly.

### Flow grouping

Hybrid inference in `analytics.ts`:

1. **Referer-first** — build parent-child chains from the `Referer` header. Time-bound to 30s to avoid stale references. Origin-only cross-origin Referers skipped as unreliable. Chain depth capped at 6.
2. **Time-based fallback** — for requests without usable Referer, group by 2s gap (`FLOW_GAP_MS`).

Each flow gets a confidence tag: `high` (Referer-derived), `medium` (time-based, ≥3 requests), `low` (time-based, <3). Rendered as a badge on the session detail page.

### What Lobe deliberately doesn't measure

- **CPU / memory / disk I/O.** Out of scope — Lobe is a request-lifecycle tool, not a system profiler. That's what `top`, `htop`, and platform APM already do.
- **Distributed trace context propagation.** Requires SDK instrumentation (W3C traceparent). Different product surface.
- **Initiator inference.** Needs browser Performance API. Different product surface.
- **HTTP/1.1 keep-alive pooling.** Not yet implemented. HTTP/1.1 traffic is always tagged `new`.

## AI-powered analysis

Lobe ships two flavors of AI diagnosis. Both are optional.

### Option 1 — `lobe explain` (CLI, any editor)

Runs from the command line against a capture export. Reads your `ANTHROPIC_API_KEY` env var, sends a summarized session to Claude Haiku 4.5 (~$0.07/call), and prints a markdown report applying the USE method.

```bash
export ANTHROPIC_API_KEY=sk-ant-...
lobe explain /tmp/lobe-capture-session-*.json
lobe explain --model sonnet /path/to/session.json    # deeper pass
lobe explain --output report.md session.json
```

### Option 2 — Claude Code / Cursor skill (no key needed)

If you already use Claude Code or Cursor, use the shipped skill and let *your* editor's AI do the diagnosis — no additional API key, no additional cost beyond what you're already paying for Claude Code / Cursor.

**One-liner install (bundled in the CLI binary):**

```bash
# from any project directory
lobe install-skill claude-code    # writes .claude/skills/lobe-diagnose/SKILL.md
lobe install-skill cursor         # writes .cursor/rules/lobe-diagnose.mdc

# or install globally for all your projects
lobe install-skill claude-code --global
lobe install-skill cursor --global

# already installed? force an overwrite to get the latest version
lobe install-skill claude-code --force
```

Then invoke:
- **Claude Code**: `/lobe-diagnose` or just ask about a slow API — Claude will pick up the skill automatically
- **Cursor**: `@lobe-diagnose` in chat

Both surfaces apply the same USE-method flow and napkin-math baselines that `lobe explain` uses under the hood.

## Testing the CLI → cloud upload loop

The web app at `apps/lobe-web` runs on Next.js + Convex + Clerk. This is how you exercise the full end-to-end path from CLI capture to the browser dashboard.

### Prereqs (one-time)

1. `apps/lobe-web/.env.local` populated with your Convex + Clerk keys (see `apps/lobe-web/README.md` if it exists, or ask in setup docs).
2. Convex running: `cd apps/lobe-web && npx convex dev`
3. Next.js running: `cd apps/lobe-web && npm run dev`
4. CLI built: `cargo build -p lobe-cli --release`
5. CLI on PATH: `ln -sf $PWD/target/release/lobe ~/.local/bin/lobe` (make sure `~/.local/bin` is on your PATH).

### The loop

**Step 1 — mint a token.**
Visit `http://localhost:3000/cli-auth` in a signed-in browser session. Give the token a name (e.g. "MacBook"), click **Mint token**, and copy the `lobe_...` value.

**Step 2 — authorize the CLI.**

```bash
lobe login \
  --token lobe_YOUR_TOKEN \
  --web-url http://localhost:3000 \
  --api-url https://<your-convex-deployment>.convex.site
```

Expected output:

```
✓  Hippocampus authorized · you@example.com (free tier)
```

The token gets saved to `~/.config/lobe/config.toml` with 0600 permissions.

**Step 3 — capture with upload enabled.**

```bash
lobe capture http://localhost:3000 --upload
```

The TUI opens listening on `127.0.0.1:7878`. In another terminal, generate traffic through the proxy:

```bash
curl http://127.0.0.1:7878/
curl http://127.0.0.1:7878/api/health
```

You should see the requests scroll into the TUI's captured list.

**Step 4 — sync to your dashboard.**
Press `u` in the TUI. Bottom-right flash message should update:

```
✓ Hippocampus synced 4 requests
```

**Step 5 — verify in the browser.**
Open `http://localhost:3000/projects`. You should see:

- A project auto-named after the upstream (e.g. `http://localhost:3000`)
- One session with the request count you captured
- Live phase-bar preview showing DNS / TCP / TLS / TTFB / download distribution
- The session in "Recent activity" at the bottom

Open `/billing` — the CLI tokens panel should list your token with `Last used just now`.

### Troubleshooting

| Symptom | Fix |
|---|---|
| `token was rejected` | Token was revoked or never saved. Re-mint at `/cli-auth`, rerun `lobe login`. |
| `upload failed to reach cloud` | `api_url` in `~/.config/lobe/config.toml` doesn't match your Convex site. Re-run `lobe login --api-url ...`. |
| Upload succeeds but `/projects` is empty | Signed into the web as a different Clerk user than the one who minted the token. Sign out + back in with the same account. |
| Clicking "Open project" 404s | Expected — the session-detail pages haven't been migrated to Convex yet (Substep 3.5). |
