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.
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
- 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
curlcommand. - 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.
Prerequisites:
- Rust stable, edition 2024 capable.
- A Chromium-family browser: Chromium, Chrome, Brave, etc.
curlfor request replay.- Optional:
nvimor another$EDITORfor body editing, SQL, and console evaluation.
git clone https://github.com/nullslate/faro.git
cd faro
cargo install --path crates/appRun the TUI against a local app:
faro http://localhost:5173Press o to launch the browser and start capture. Use eager launch when you want capture to start immediately:
faro --launch-on-start http://localhost:5173Open a previously captured database without launching a browser:
faro tui ~/.config/faro/faro.dbcargo run -- http://localhost:5173
cargo run -- --launch-on-start http://localhost:5173chromium --remote-debugging-port=9222 --user-data-dir=/tmp/faro-profile
faro --attach-port 9222 http://localhost:5173Use FARO_BROWSER=/path/to/chrome-or-chromium to override browser discovery.
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=nvimGUI 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.
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.
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 --jsonInspect 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 --vacuumReplay and query:
faro replay <request-id> --json
faro sql "select * from requests where status_code >= 500" --jsonClear captured sessions and their cascaded request/console/storage/cookie/replay rows:
faro sessions nuke --yesTrim one noisy session while keeping recent data:
faro sessions prune <session-id> --max-requests 5000 --max-repeated 25 --max-console 2000 --max-ws 5000Compact the database after deleting old sessions or pruning lots of captured data:
faro db stats
faro db compact --vacuumfaro 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.
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 mcpEnable 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 mcpcopy_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 mcpMCP 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"]
}
}
}- Start Faro normally while reproducing the issue. If you intentionally enabled
--mcp-allow-mutation, the agent can also runcapture_url. - Have the agent call
list_sessionsand pick the rightsession_idwhen more than one capture exists. - Ask the agent to list failing or slow requests with
list_requests. - Have it inspect a request with
get_requestandget_response_body. - Let it check console failures with
list_console_errors. - Use
copy_request_as_curlfor a redacted reproduction command. Enable sensitive MCP mode only when the raw body/credentials are intentionally needed. - Use
replay_requestonly after starting MCP with--mcp-allow-mutation. - Use
run_readonly_sqlfor 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.
| 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. |
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: trueand--mcp-allow-sensitive. - MCP body responses are size-capped by
redaction.mcp_body_limit_bytes. - Security-relevant actions are appended to
audit.jsonlin 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 = 262144The 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" --jsonThe importable agent package lives in:
agents/faro/
It contains:
SKILL.md: workflow instructions for agent tools.mcp.json: ready-to-copy MCP server config.
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.
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.
Run the standard checks:
cargo fmt --check
cargo test
cargo clippy --all-targets --all-features -- -D warnings
rg -n "\.ok\(\)|\.unwrap\(\)|\.expect\(" cratesRun locally:
cargo run -- http://localhost:5173
cargo run -- capture http://localhost:5173 --for 10s --json
cargo run -- --db /tmp/faro.db mcpRun the opt-in performance harnesses:
scripts/perf-smoke.sh
cargo test large_session -- --ignored --nocapture
cargo test render_perf -- --ignored --nocaptureUseful 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.
Replay polish is next: selectable replay history, compare-any replay diffs, replay response metadata, and copy/export actions for replay results.
- 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-Cookieobservation plus a page-sidedocument.cookieobserver. - MCP browser operations require an attached CDP websocket URL from
capture_url; database inspection works offline.
MIT
