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
| Command | What it does |
|---|---|
faz init | Generate a starter faz.yaml and .faz/ directory. |
faz serve | Start the REST API on 127.0.0.1:8787. |
faz add-database | Interactive wizard that appends one database to faz.yaml. |
faz query "<sql>" | Run a single query through the safety pipeline and pretty-print results. |
faz test | Probe the safety pipeline against every configured database. |
faz logs | Pretty-print or tail the audit JSONL. |
faz policy | Print the loaded permission tree from faz.yaml. |
faz mcp | Run the MCP stdio server (used by Claude Desktop / Cursor / etc.). |
faz mcp install | Write 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.
| Flag | Default | Notes |
|---|---|---|
--force | off | Overwrite an existing faz.yaml without asking. |
faz inituv run faz initpython -m faz initWithout --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.
| Flag | Default | Notes |
|---|---|---|
--host | 127.0.0.1 | Bind address. Anything off-loopback prints a no-built-in-auth warning. |
--port | 8787 | Bind port. |
faz serveuv run faz servepython -m faz serveBy 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-databaseuv run faz add-databasepython -m faz add-databaseThis 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.
| Argument | Required | Notes |
|---|---|---|
<query> | yes | The query body. Quote it. |
--database | yes | Connector name from faz.yaml. |
--table | no | Declared target table. Improves safety logging. |
--language | no | sql / 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,GUARDRAILSA 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 noneAudit entries from this surface have transport="cli".
faz test
Builds the runtime and runs four probes per configured database:
- Reachable — calls
list_databases. - Read probe — runs a
SELECT-style query against the first discovered table. Expected to pass forR / RW / RA / RWA / A, blocked forWor no policy. - Write probe —
INSERT-style against a synthetic name (faz_synthetic_does_not_exist). Expected to be blocked forR-only baselines, allowed-but-failing-on-name for write baselines. - DDL probe —
DROP-style against the synthetic name. Allowed only forA.
faz testuv run faz testpython -m faz testOutput:
✓ 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.
| Flag | Default | Notes |
|---|---|---|
--follow, -f | off | Tail the file. Survives log rotation by inode. |
--path | .faz/audit.jsonl | Audit JSONL path. Override when FAZ_AUDIT_LOG points elsewhere. |
faz logs # print every entry
faz logs --follow # tail -F semanticsEach 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 SELECTFor 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 policyuv run faz policypython -m faz policyOutput 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 = 30Use 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 mcpuv run faz mcppython -m faz mcpIt 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.
| Flag | Default | Notes |
|---|---|---|
--target | all | One of claude, cursor, openclaw, all. Picks which client config to write. |
--dry-run | off | Print 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.jsonDefault 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.jsonfor 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: writtenFor per-client integration walkthroughs (verifying the connection, troubleshooting), see Integrations.
Related
faz.yaml— the config the CLI reads.- Runtime options — env vars that affect every command.
- REST endpoints — the HTTP surface
faz serveexposes. - MCP tools — the tool surface
faz mcpexposes to AI clients.