faz
Usage

CLI

Every faz command — what it does, what flags it takes, and what its output looks like.

The faz CLI is the entry point for everything: generating config, running the server, querying ad-hoc, tailing the audit log, installing MCP integrations. This page documents each command, its flags, and what to expect.

For a guided first-time setup using these commands, see Quickstart.

Command overview

CommandWhat it does
faz initGenerate a starter faz.yaml and .faz/ directory.
faz serveStart the REST API on 127.0.0.1:8787.
faz add-databaseInteractive wizard that appends one database to faz.yaml.
faz query "<sql>"Run a single query through the safety pipeline and pretty-print results.
faz testProbe the safety pipeline against every configured database.
faz logsPretty-print or tail the audit JSONL.
faz policyPrint the loaded permission tree from faz.yaml.
faz mcpRun the MCP stdio server (used by Claude Desktop / Cursor / etc.).
faz mcp installWrite MCP config to Claude Desktop, Cursor, OpenClaw, or a custom path.

faz init

Generates a starter faz.yaml and a .faz/ runtime directory in the current working directory.

FlagDefaultNotes
--forceoffOverwrite an existing faz.yaml without asking.
faz init
uv run faz init
python -m faz init

Without --force and with an existing faz.yaml, the command refuses:

Error: /your/cwd/faz.yaml already exists. Pass --force to overwrite.

The generated template is heavily commented; the comments explain what each section is for. See faz.yaml for the schema reference.

faz serve

Starts the FastAPI REST server.

FlagDefaultNotes
--host127.0.0.1Bind address. Anything off-loopback prints a no-built-in-auth warning.
--port8787Bind port.
faz serve
uv run faz serve
python -m faz serve

By default, listens on 127.0.0.1:8787. Binding off-loopback prints:

warning: binding '0.0.0.0' exposes faz off-loopback. faz has
         no built-in remote auth — put a reverse proxy with
         your own auth (nginx / Cloudflare Tunnel / Tailscale)
         in front before going to prod.

faz has no built-in remote auth. If you bind off-loopback, front it with a reverse proxy that does auth. See REST API.

faz add-database

Interactive wizard. Asks for a connector type, then walks through type-aware prompts (host, port, database, credentials, any connector-specific extra: keys), and appends the resulting entry to faz.yaml. Comments and ordering in the existing file are preserved.

faz add-database
uv run faz add-database
python -m faz add-database

This is the friendly path. If you'd rather edit faz.yaml directly, the per-connector field reference is on each database connector page.

faz query

Runs one query through the safety pipeline and pretty-prints the result. Builds the runtime in-process — no separate server needed — so expect 5–10 seconds of connector-init time on each invocation. For batches of queries, run faz serve and use curl instead.

ArgumentRequiredNotes
<query>yesThe query body. Quote it.
--databaseyesConnector name from faz.yaml.
--tablenoDeclared target table. Improves safety logging.
--languagenosql / mql / cypher / es_dsl / vector / dynamo / couchdb. Inferred from connector type when omitted.
faz query "SELECT * FROM <table> LIMIT 5" --database <database> --table <table>
uv run faz query "SELECT * FROM <table> LIMIT 5" --database <database> --table <table>
python -m faz query "SELECT * FROM <table> LIMIT 5" --database <database> --table <table>

Replace <database> with the connector name from faz.yaml and <table> with the table you want to read.

A successful query prints an aligned ASCII table plus a one-line footer with row count, latency, and stages passed:

<column>  <column>  <column>
--------  --------  --------
<value>   <value>   <value>
<value>   <value>   <value>
<value>   <value>   <value>
<value>   <value>   <value>
<value>   <value>   <value>

  5 row(s) in 23.4ms — stages: PROMPT_GUARD,RBAC_GATE,AST_CHECKER,INJECTION_ANALYSER,GUARDRAILS

A blocked query prints BLOCKED plus the stage and reason, and exits with code 2:

BLOCKED
  stage:      RBAC_GATE
  reason:     Access denied: <database>.<table> requires permission for SELECT; policy grants none

Audit entries from this surface have transport="cli".

faz test

Builds the runtime and runs four probes per configured database:

  1. Reachable — calls list_databases.
  2. Read probe — runs a SELECT-style query against the first discovered table. Expected to pass for R / RW / RA / RWA / A, blocked for W or no policy.
  3. Write probeINSERT-style against a synthetic name (faz_synthetic_does_not_exist). Expected to be blocked for R-only baselines, allowed-but-failing-on-name for write baselines.
  4. DDL probeDROP-style against the synthetic name. Allowed only for A.
faz test
uv run faz test
python -m faz test

Output:

✓ list_databases — 2 configured

Per-DB safety probes  (2 configured)
  <database-1> (postgresql)  — policy: R
    ✓ reachable — 18 tables
    ✓ read on '<table>' (allowed) — allowed
    ✓ write blocked (target=faz_synthetic_does_not_exist) — at RBAC_GATE
    ✓ DDL blocked (target=faz_synthetic_does_not_exist) — at RBAC_GATE

  <database-2> (mongodb)  — policy: R
    ✓ reachable — 4 tables
    ✓ read on '<collection>' (allowed) — allowed
    ✓ write blocked (target=faz_synthetic_does_not_exist) — at RBAC_GATE
    ✓ DDL blocked (target=faz_synthetic_does_not_exist) — at RBAC_GATE

  9/9 probes passed.

Exit code is 0 if every probe matches its expected outcome, 1 otherwise.

For schema-on-write databases (MongoDB, CouchDB) where the policy permits writes, faz test will create a faz_synthetic_does_not_exist collection holding {"_faz_test": true}. Drop it manually if you don't want it. Schema-on-read databases (Postgres, MySQL, Oracle, etc.) just error with "table not found" — no data is written.

faz logs

Pretty-prints or tails the audit JSONL.

FlagDefaultNotes
--follow, -foffTail the file. Survives log rotation by inode.
--path.faz/audit.jsonlAudit JSONL path. Override when FAZ_AUDIT_LOG points elsewhere.
faz logs            # print every entry
faz logs --follow   # tail -F semantics

Each entry prints as one line with trace id, transport, stage outcomes, rows returned, and latency. A separate error: line follows when a request failed mid-execution:

req_a1b2c3d4e5f6  [mcp/stdio ]  rbac=PASS ast=PASS inj=PASS  rows=42  total=23ms
req_b2c3d4e5f6a7  [rest/local]  rbac=BLOCK ast=PASS inj=PASS  rows=0  total=4ms
    error: Access denied: <database>.<sensitive-table> requires permission for SELECT

For deeper queries, parse the JSONL directly with jq. See Audit log for the schema and example queries.

faz policy

Prints the loaded permission tree.

faz policy
uv run faz policy
python -m faz policy

Output groups by database — baseline first, then per-table overrides — followed by the global safety: block:

<database-2>
    baseline: R

<database-1>
    baseline: R
    overrides:
        <audit-table>     →  W
        <writable-table>  →  RW

safety:
    max_rows_per_query     =  5000
    query_timeout_seconds  =  30

Use this to confirm faz.yaml parses cleanly and to verify what's actually loaded after edits. (faz.yaml doesn't hot-reload — restart faz serve and re-run faz policy to see changes take effect.)

faz mcp

Runs the MCP stdio server. Used by AI clients (Claude Desktop, Cursor, etc.) — they spawn this process per session and communicate over stdio. You almost never invoke it directly; use faz mcp install to wire it into a client.

faz mcp
uv run faz mcp
python -m faz mcp

It blocks, reading MCP framing on stdin until the client disconnects.

Audit entries from this surface have transport="mcp/stdio".

faz mcp install

Writes the MCP server entry to a client's config so the client knows to spawn faz mcp.

FlagDefaultNotes
--targetallOne of claude, cursor, openclaw, all. Picks which client config to write.
--dry-runoffPrint the JSON that would be written, without touching files.
--path(auto)Override auto-detection. Write to a specific file. Useful for non-standard installs and previewing.
faz mcp install                              # write to every detected client
faz mcp install --target claude              # only Claude Desktop / Code
faz mcp install --target cursor --dry-run    # preview Cursor's config
faz mcp install --target openclaw --path /tmp/openclaw.json

Default config locations per target:

  • Claude Desktop / Code: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS), ~/.config/Claude/claude_desktop_config.json (Linux), %APPDATA%\Claude\claude_desktop_config.json (Windows).
  • Cursor: per-OS Cursor config + ~/.cursor/mcp.json for project-scoped.
  • OpenClaw: ~/.openclaw/openclaw.json.

Output looks like:

[claude] /Users/you/Library/Application Support/Claude/claude_desktop_config.json: written
[cursor] /Users/you/Library/Application Support/Cursor/User/settings.json: written

For per-client integration walkthroughs (verifying the connection, troubleshooting), see Integrations.

On this page