Skip to content

nullslate/faro

Repository files navigation

Faro

License: MIT Rust MCP

Faro is a terminal-first browser debugging workbench for frontend and full-stack development.

It launches or attaches to a Chromium-family browser through the Chrome DevTools Protocol, captures browser observations into SQLite, and lets humans and agents inspect the result through a TUI, CLI, SQL, or MCP.

Faro terminal UI showing filtered network requests, request details, and a JSON response body.

Browser -> Chrome DevTools Protocol -> Faro capture -> SQLite -> TUI / CLI / MCP

Faro does not embed Chromium and does not render web pages in the terminal. It controls a real browser and keeps a durable local debugging database.

Links: Contributing · Security · Code of Conduct · MIT License

Features

  • Network request tree with route drilldown, filters, presets, response details, and replay history.
  • WebSocket frame stream view with sent/received traffic, payload inspection, and filtering.
  • Captured request/response headers, query params, request bodies, bounded text response bodies, SSE parsing, and small image previews where the terminal supports inline images.
  • Console logs, page errors, and JavaScript scratch evaluation through $EDITOR.
  • Storage and cookies views with captured snapshots and live mutation events.
  • Copy any captured request as a full curl command.
  • Replay captured requests with curl, persist replay status/body metadata, and review replay history in the request detail pane.
  • Copy a redacted Markdown share bundle for a selected request when you need to send context to another person or agent.
  • Read-only SQL editor in the TUI, plus CLI/MCP read-only SQL for agents.
  • Agent-friendly CLI and MCP server for capture, inspection, replay, and SQL.
  • TOML config in the platform config directory, with relative DB paths resolved there.

Quick Start

Install From Source

Prerequisites:

  • Rust stable, edition 2024 capable.
  • A Chromium-family browser: Chromium, Chrome, Brave, etc.
  • curl for request replay.
  • Optional: nvim or another $EDITOR for body editing, SQL, and console evaluation.
git clone https://github.com/nullslate/faro.git
cd faro
cargo install --path crates/app

Run the TUI against a local app:

faro http://localhost:5173

Press o to launch the browser and start capture. Use eager launch when you want capture to start immediately:

faro --launch-on-start http://localhost:5173

Open a previously captured database without launching a browser:

faro tui ~/.config/faro/faro.db

Run Without Installing

cargo run -- http://localhost:5173
cargo run -- --launch-on-start http://localhost:5173

Attach To An Existing Browser

chromium --remote-debugging-port=9222 --user-data-dir=/tmp/faro-profile
faro --attach-port 9222 http://localhost:5173

Use FARO_BROWSER=/path/to/chrome-or-chromium to override browser discovery.

Editor Handoff

Faro opens $EDITOR for SQL, console evaluation, body viewing/editing, storage edits, cookie edits, and replay editing. Terminal editors usually work as-is:

export EDITOR=nvim

GUI editors should be configured to wait until the file is closed:

export EDITOR="code --wait"
export EDITOR="zed --wait"

Without a wait flag, GUI editors may return immediately and Faro will resume before the file is saved.

TUI Basics

Common keys:

q/esc   quit
tab     switch focus
1-6     switch views: Network, Console, WebSockets, Scripts, Storage, Cookies
j/k     move focused selection
enter   drill into a route / expand selected tree item
backspace go up one route level
h/l     switch request detail tab
p       open command palette
o       open browser and start capture
y       copy selected request as curl
w       save selected request/response exchange to /tmp
r       replay selected request with curl
R       edit selected request in $EDITOR, then replay
D       diff original response body against latest replay response body
s       cycle request sort
S       open sessions manager
f       cycle quick network filter preset
e       open selected item in $EDITOR
u/d     scroll focused detail/body pane
g/G     jump to top/bottom in focused pane
/       filter requests
?       floating key/filter help
c       clear request filter

The sessions manager lets you switch between captured sessions and delete old sessions from the database. Press S, move with j/k, press enter to open a session, or x to delete the selected session.

Useful palette commands:

  • Copy Share Bundle: copy a redacted Markdown request summary with headers, body previews, and replay history.
  • Sessions: Browse: switch between or delete captured sessions with request/error/replay/storage counts.
  • Debug: Toggle Perf: show frame, poll, capture, replay, and detail-load timing while tuning performance.

Request filters support plain text, structured fields, and case-insensitive regex patterns:

method:post
status:2xx
status:404
type:fetch
domain:localhost
url:/api/users
path:/api
mime:json
header:x-request-id
body:error
reqbody:email
resbody:database
has:body
has:error
has:replay
duration:>500
size:>100kb
api/(users|teams)
path:/api/v[0-9]+
method:^(post|put)$

Quick presets include all, errors, JSON, fetch, XHR, SSE, images, scripts, styles, documents, with body, slow, large, and replayed.

CLI

The CLI is designed for humans and agents that want to inspect Faro without opening the TUI.

Capture a page without the TUI:

faro capture https://example.com --for 15s --json

Inspect captured requests:

faro requests --route /api --filter "status >= 400" --json
faro request get <request-id> --body --json
faro request curl <request-id>

Inspect browser state:

faro console errors --json
faro storage get localStorage auth --json
faro cookies list --json
faro sessions list --json
faro sessions prune <session-id> --max-requests 5000 --max-repeated 25 --max-console 2000 --max-ws 5000
faro db stats
faro db compact --vacuum

Replay and query:

faro replay <request-id> --json
faro sql "select * from requests where status_code >= 500" --json

Clear captured sessions and their cascaded request/console/storage/cookie/replay rows:

faro sessions nuke --yes

Trim one noisy session while keeping recent data:

faro sessions prune <session-id> --max-requests 5000 --max-repeated 25 --max-console 2000 --max-ws 5000

Compact the database after deleting old sessions or pruning lots of captured data:

faro db stats
faro db compact --vacuum

faro sessions compact --vacuum is kept as a compatible alias. Compaction deletes orphaned body rows and, with --vacuum, checkpoints WAL and asks SQLite to reclaim file space. faro db stats reports database/WAL sizes, body byte totals, top sessions by captured weight, top repeated request groups, and table row counts so you can see whether payloads or metadata are driving growth.

Route filters accept:

  • /api/users: exact route and descendants.
  • /api/users/:id: one dynamic path segment.
  • /api/*: wildcard for the rest of the path.

Use --db <path> with any command to target a specific SQLite database.

MCP And Agent Integration

Faro includes a stdio MCP server so coding agents can inspect the same browser capture database that the TUI and CLI use. The server is DB-first: tools read from SQLite and do not scrape the terminal UI. Mutating or browser-driving tools are disabled by default.

faro mcp

Enable mutating tools only when you want an agent to launch/attach capture, delete sessions, replay requests, reload pages, or evaluate JavaScript:

faro --mcp-allow-mutation mcp

copy_request_as_curl redacts sensitive headers and omits request bodies by default. To allow agents to request full credentials and bodies with include_sensitive: true, start MCP with:

faro --mcp-allow-sensitive mcp

MCP body-returning tools cap returned body text using redaction.mcp_body_limit_bytes from config.toml and include truncation metadata when a body is clipped.

Example MCP config:

{
  "mcpServers": {
    "faro": {
      "command": "faro",
      "args": ["mcp"]
    }
  }
}

Most users should point their agent at the default config database. Use an explicit database when you want a project-specific capture or a disposable debugging session:

{
  "mcpServers": {
    "faro": {
      "command": "faro",
      "args": ["--db", "/path/to/faro.db", "mcp"]
    }
  }
}

Typical Agent Workflow

  1. Start Faro normally while reproducing the issue. If you intentionally enabled --mcp-allow-mutation, the agent can also run capture_url.
  2. Have the agent call list_sessions and pick the right session_id when more than one capture exists.
  3. Ask the agent to list failing or slow requests with list_requests.
  4. Have it inspect a request with get_request and get_response_body.
  5. Let it check console failures with list_console_errors.
  6. Use copy_request_as_curl for a redacted reproduction command. Enable sensitive MCP mode only when the raw body/credentials are intentionally needed.
  7. Use replay_request only after starting MCP with --mcp-allow-mutation.
  8. Use run_readonly_sql for deeper analysis across the capture database.

Useful prompts:

Use Faro to find failed network requests from the latest capture. Inspect the response bodies and summarize the likely backend or frontend issue.
Use Faro to inspect requests under /api during the latest session. Find slow calls, replay the most suspicious request if safe, and show me the curl command.
Use Faro read-only SQL to group captured requests by domain and status code. Highlight third-party failures and any unusually large responses.

MCP Tools

Tool Purpose
capture_url Launch or attach to Chromium and capture a URL for a bounded duration. Requires --mcp-allow-mutation.
list_sessions List capture sessions with request/error/replay/storage counts.
get_session Get one capture session and summary counts.
get_db_stats Return DB maintenance stats, including body totals, heavy sessions, repeated request groups, and table counts.
list_heavy_sessions List sessions ordered by captured body bytes and request volume.
list_repeated_requests List repeated method/type/url groups that are likely to make a session noisy or large.
prune_session Prune one session with retention limits; requires confirm: true and --mcp-allow-mutation.
delete_all_sessions Delete all sessions and cascaded captured data; requires confirm: true and --mcp-allow-mutation.
list_requests List captured requests, with route/filter support for narrowing results. Accepts optional session_id.
get_request Fetch request metadata, headers, response metadata, and body references.
get_response_body Load a captured response body by request id.
list_replays List replay records by session or request.
get_replay Get one replay record and optionally its stored body.
list_websocket_frames List captured WebSocket frames with direction/opcode filters.
list_console_errors Return captured browser console errors. Accepts optional session_id.
list_storage_items List current localStorage/sessionStorage items. Accepts optional session_id and filters.
get_storage_item Read a current localStorage or sessionStorage item.
list_cookies List current captured cookies. Accepts optional session_id.
copy_request_as_curl Return a redacted curl command by default. include_sensitive: true requires --mcp-allow-sensitive.
replay_request Replay a captured request and persist replay metadata. Requires --mcp-allow-mutation.
evaluate_js Evaluate JavaScript through a CDP websocket URL returned by capture_url. Requires --mcp-allow-mutation.
reload_page Reload the attached page through a CDP websocket URL returned by capture_url. Requires --mcp-allow-mutation.
run_readonly_sql Run guarded read-only SQL against the Faro SQLite database.

Security Defaults

Faro is designed to capture sensitive browser debugging data, so sharing and agent access are guarded:

  • Redacted share bundles hide sensitive headers and redact matching JSON body fields before copying.
  • MCP runs read-only by default for risky tools. Browser-driving and mutating tools require --mcp-allow-mutation.
  • MCP curl output is redacted by default. Raw credentials and bodies require both include_sensitive: true and --mcp-allow-sensitive.
  • MCP body responses are size-capped by redaction.mcp_body_limit_bytes.
  • Security-relevant actions are appended to audit.jsonl in the Faro config directory.

Redaction rules are configurable in config.toml:

[redaction]
header_names = ["authorization", "cookie", "set-cookie", "x-api-key"]
json_key_patterns = ["auth", "email", "password", "secret", "token"]
text_patterns = ["bearer ", "token=", "password=", "secret="]
mcp_body_limit_bytes = 262144

The same workflows are available from the CLI when an agent cannot use MCP:

faro capture https://example.com --for 15s --json
faro requests --filter "status >= 400" --json
faro request get <request-id> --body --json
faro request curl <request-id>
faro console errors --json
faro sql "select status_code, count(*) from requests group by status_code" --json

The importable agent package lives in:

agents/faro/

It contains:

  • SKILL.md: workflow instructions for agent tools.
  • mcp.json: ready-to-copy MCP server config.

Configuration

On first run, Faro creates config.toml in the platform config directory:

Linux:   $XDG_CONFIG_HOME/faro/config.toml or ~/.config/faro/config.toml
macOS:   ~/Library/Application Support/faro/config.toml
Windows: %APPDATA%\faro\config.toml

The default database path is faro.db relative to the config directory. Faro also stores layout preferences, the last SQL query, and the persistent Chromium profile there.

Linux:   ~/.config/faro/faro.db
macOS:   ~/Library/Application Support/faro/faro.db
Windows: %APPDATA%\faro\faro.db

Important config fields:

[app]
db_path = "faro.db"
launch_on_start = false

[ui]
bottom_fade_rows = 3
max_body_tree_items = 2000

[theme]
text = "#d4be98"
muted = "#928374"
accent = "#89b482"
panel_title = "#d8a657"
panel_border = "#3c3836"
active_border = "#89b482"

The default theme is Gruvbox-inspired and can be customized with hex colors or supported terminal color names.

Architecture

Workspace crates:

  • faro-core: domain models and event types.
  • faro-store: SQLite event store, projections, and read-only SQL guardrails.
  • faro-capture: source-neutral ingestion pipeline.
  • faro-cdp: Chrome DevTools Protocol capture/control plane.
  • faro: CLI, TUI, and MCP entrypoint.

Captured data is persisted in SQLite so the TUI, CLI, SQL, and MCP all inspect the same source of truth.

Development

Run the standard checks:

cargo fmt --check
cargo test
cargo clippy --all-targets --all-features -- -D warnings
rg -n "\.ok\(\)|\.unwrap\(\)|\.expect\(" crates

Run locally:

cargo run -- http://localhost:5173
cargo run -- capture http://localhost:5173 --for 10s --json
cargo run -- --db /tmp/faro.db mcp

Run the opt-in performance harnesses:

scripts/perf-smoke.sh
cargo test large_session -- --ignored --nocapture
cargo test render_perf -- --ignored --nocapture

Useful rough targets on a development machine are sub-frame request filtering at 25k rows, low-single-digit millisecond live DB deltas, and render paths under a 16ms frame budget. Treat these as regression checks rather than portable benchmarks.

Next Up

Replay polish is next: selectable replay history, compare-any replay diffs, replay response metadata, and copy/export actions for replay results.

Current Limitations

  • Faro currently targets Chromium-family browsers through CDP.
  • Response body capture is bounded to avoid unbounded database growth.
  • Storage mutation tracking is CDP DOMStorage-based; snapshots are used for baseline and reconciliation.
  • Cookie mutation tracking uses HTTP Set-Cookie observation plus a page-side document.cookie observer.
  • MCP browser operations require an attached CDP websocket URL from capture_url; database inspection works offline.

License

MIT

About

Faro is a terminal-first browser debugging workbench for frontend and full-stack development.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages