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.
Install
Two paths, whichever fits your setup.
# macOS + Linux via Homebrew
# Any platform with the Rust toolchain (macOS, Linux, Windows, WSL, Alpine, …)
Verify:
No account required to run any of the CLI commands below.
Quickstart
Point Lobe at a local dev server and route traffic through it:
That spins up a local proxy on http://127.0.0.1:7878 by default (configurable via --listen). 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— quite— export the current session to/tmp/lobe-capture-session-*.jsonu— upload session to your Lobe account (requireslobe loginfirst)
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.
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?"
Capturing a local web app
Then browse through the proxy at http://127.0.0.1:7878 instead of localhost:5173 directly.
Two details worth knowing:
localhostvs127.0.0.1is not interchangeable. Match whatever your app normally answers on — different hosts, different DNS paths.- Change the proxy port with
--listen 127.0.0.1:9090if7878is 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.
Then point the mobile app at the proxy:
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:
/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:
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:
Then open:
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.
- Export a capture session from the CLI with
e - Copy the JSON into:
apps/lobe-web/data/imported-capture-sessions.json
The file shape is:
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:
- Referer-first — build parent-child chains from the
Refererheader. Time-bound to 30s to avoid stale references. Origin-only cross-origin Referers skipped as unreliable. Chain depth capped at 6. - 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.
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):
# from any project directory
# or install globally for all your projects
# already installed? force an overwrite to get the latest version
Then invoke:
- Claude Code:
/lobe-diagnoseor just ask about a slow API — Claude will pick up the skill automatically - Cursor:
@lobe-diagnosein 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)
apps/lobe-web/.env.localpopulated with your Convex + Clerk keys (seeapps/lobe-web/README.mdif it exists, or ask in setup docs).- Convex running:
cd apps/lobe-web && npx convex dev - Next.js running:
cd apps/lobe-web && npm run dev - CLI built:
cargo build -p lobe-cli --release - CLI on PATH:
ln -sf $PWD/target/release/lobe ~/.local/bin/lobe(make sure~/.local/binis 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.
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.
The TUI opens listening on 127.0.0.1:7878. In another terminal, generate traffic through the proxy:
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). |