# flowifai — full documentation > flowifai gates risky MCP tool calls with human approval. Credentials stay on the > local tools host; the cloud backend is an approval authority, never a credential holder. Curated index: https://flowif.ai/llms.txt Each section below carries its canonical Source URL; the same content is served as raw markdown at https://flowif.ai/docs/.md --- Source: https://flowif.ai/docs/install/ Markdown: https://flowif.ai/docs/install.md # Install flowifai One signed installer puts the operator CLI, local broker, and stdio wrapper under `~/.flowifai/bin`: ```sh curl -fsSL https://flowif.ai/install.sh | sh ``` Linux and macOS on amd64 or arm64 are supported. ## Inspect first Use the inspect-first path when you want to review the script before running it: ```sh curl -fsSLO https://flowif.ai/install.sh less install.sh sh install.sh ``` The installer reads signed `stable.json` release metadata, verifies the manifest signature and SHA values, verifies each downloaded asset's SHA-256 checksum, and only then puts binaries in place — with an atomic version swap and `--version` post-checks. The release-key fingerprint is carried in the signed metadata rather than copied into this page. ## What gets installed | Binary | Role | | --- | --- | | `flowifai` | Operator CLI for connect, status, doctor, update, policy, and approvals. | | `flowifai-broker` | Trusted local process on the tools host. It owns the enrollment credential and launches the real MCP server. | | `flowifai-wrapper` | Untrusted stdio MCP shim used by Claude or another MCP client. It holds no flowifai credential. | ## How it fits together The full first-run flow is in the [quickstart](/docs/quickstart/). For existing Claude Code or Cursor MCP configuration, start with: ```sh flowifai setup --dry-run flowifai setup ``` The wizard creates one private profile and loopback broker process for each distinct selected MCP launch. The older `flowifai connect` flow remains available for manual and unsupported-client setup. - Profile brokers bind an ephemeral `127.0.0.1` port and publish it to their matching wrappers through private runtime state. - The legacy singleton broker binds to `127.0.0.1:8090` by default. - The wrapper-facing broker endpoints are not authenticated. Do not expose them directly to a LAN, VPN, or raw firewall allow-list. - For split-host setups, use an SSH tunnel or equivalent authenticated encrypted loopback tunnel, then point `FLOWIFAI_BROKER_URL` at the local tunnel endpoint. - Prefer the app-generated `flowifai connect --pairing-token-stdin -- ...` command over legacy `FLOWIFAI_WRAPPED_CMD`; the connect path stores the wrapped command as structured argv. ## Wrapper path for GUI clients GUI-launched MCP clients may not inherit your shell `PATH`. Print the exact Claude JSON or the expanded wrapper path: ```sh flowifai client-config claude --print ``` ```sh flowifai wrapper-path ``` ## Broker service Keep the broker running after the terminal closes with the per-user service helpers: ```sh flowifai broker install-service flowifai broker start flowifai broker logs ``` On Linux this writes a systemd user unit. On macOS this writes a LaunchAgent. The service runs `flowifai-broker` from the installed tools directory, reads the private local config created by `flowifai connect`, and snapshots the current shell environment into a private service env file, excluding flowifai runtime overrides. If your MCP server needs new API keys or PATH entries later, rerun `flowifai broker install-service` before restarting the broker. If flowifai backend, credential, broker address, server name, or wrapped command settings change, rerun the app-generated `flowifai connect` command. Setup-managed services use a profile ID: ```sh flowifai broker status --profile PROFILE_ID flowifai broker logs --profile PROFILE_ID flowifai broker reload --profile PROFILE_ID ``` Use `--all` instead of `--profile` to operate all protected profiles. Each profile has separate private config, credentials, runtime identity, and process state. ## Update and verify ```sh flowifai version flowifai update --check flowifai update ``` ```sh flowifai doctor --strict flowifai doctor --json --check-update ``` Use `flowifai doctor --strict` for install, backend, and broker diagnostics that fail decisively. Use `--check-update` only when you want to diagnose the signed release channel. The updater verifies the same signed manifest and per-asset checksums as the installer before replacing binaries. `flowifai status` is an operator diagnostic. Registered-server data uses the local config or `FLOWIFAI_S2S_SECRET`; approval data can require `FLOWIFAI_USER_TOKEN`. ## Common fixes | Problem | Fix | | --- | --- | | Command not found | Add `~/.flowifai/bin` to `PATH` or use the absolute binary path. | | Missing S2S secret | Run the app-generated `flowifai connect --pairing-token-stdin` command. If pairing is disabled or the credential is lost, rotate/mint a replacement credential in agent details and use the credential-stdin fallback. Credentials are shown once. | | Wrapper cannot reach broker | Same host: run `flowifai broker start` and use `FLOWIFAI_BROKER_URL=http://localhost:8090`. Split host: start the loopback tunnel first and point the wrapper at that local endpoint. | | Schema drift | Run the app-generated `flowifai connect` command again, then `flowifai broker stop && flowifai broker start` so startup drift checks re-run. | | Stale install lock | Remove `$FLOWIFAI_HOME/.install.lock` only after confirming no install or update is running. | | Unsupported platform | Linux and macOS on amd64 or arm64 are supported. | More failure modes — including pairing-token expiry — are in [troubleshooting](/docs/troubleshooting/). ## Security note The broker is the trust anchor; the wrapper holds no flowifai credential. Credentials configured in your local MCP server stay local, but tool names, descriptions, input schemas, call arguments, approval events, audit metadata, lifecycle events, and execution results may be sent to and stored by the backend. Values embedded in arguments or results, including accidental secrets, may therefore be stored. Read the [security model](/docs/security-model/). --- Source: https://flowif.ai/docs/setup/ Markdown: https://flowif.ai/docs/setup.md # One-shot MCP setup The setup wizard finds MCP servers already configured in Claude Code and Cursor, lets you choose which ones to protect, and installs the flowifai wrapper in their place. One run can onboard several MCPs. Start with the no-side-effects preview: ```sh flowifai setup --dry-run ``` Then run the interactive setup: ```sh flowifai setup ``` The interactive picker starts with every eligible MCP selected. Use the arrow keys to move, space to toggle a selection, and enter to continue. You can also use: ```sh flowifai setup --all flowifai setup --all --yes flowifai setup --accessible flowifai setup --backend https://app.staging.flowif.ai ``` `--all` selects every supported result without opening the picker. `--yes` skips the final terminal confirmation, but does not skip browser authentication or health checks. `--accessible` (or `FLOWIFAI_ACCESSIBLE=1`) replaces the interactive picker with numbered, line-oriented prompts. Setup currently defaults to `https://app.staging.flowif.ai` while the production application remains behind its launch gate. `--backend` is available for self-hosted and local development deployments. ## What the wizard discovers | Client | Scope | Configuration read | | --- | --- | --- | | Claude Code | User | `~/.claude.json` | | Claude Code | Project | `.mcp.json` in the current project | | Claude Code | Local | The current project's local entries in `~/.claude.json` | | Cursor | User | `~/.cursor/mcp.json` | | Cursor | Project | `.cursor/mcp.json` in the current project | Run setup from the project whose project or local entries you want to discover. Discovery is read-only and does not invoke either client. The wizard supports local stdio MCP launches with a command, ordered arguments, optional working directory, and environment values that can be frozen. It lists unsupported entries with a reason. Remote HTTP, SSE, WebSocket, dynamic-input, or ambiguous definitions are not silently converted. Claude entries with literal environment values are also refused because Claude's native mutation CLI would place those values in process arguments; inherited `${NAME}` references remain supported. Claude Desktop, VS Code, Codex, Windsurf, and other MCP clients are not modified by this release. Their discovery and mutation paths are independently capability-gated; use the [manual setup](#manual-or-unsupported-clients) until support for a client and scope is explicitly listed here. ## What dry run reveals Dry run prints the client, scope, source path, executable name, argument count, working directory, and environment variable names. It does not print argument values, environment values, raw configuration, or credentials. It also does not start MCP servers, open a browser, make network requests, or change client or flowifai files. Use it before setup when reviewing a shared or sensitive machine. ## What happens after confirmation For each distinct selected launch, setup: 1. Inspects the real MCP server and requires it to expose at least one tool. 2. Opens a browser for flowifai authentication. If auto-open fails, the terminal still prints the URL. 3. Creates one protected profile with a server-generated credential. 4. Writes private profile configuration and frozen environment data locally. 5. Installs and starts a separate profile broker, then waits for its identity and tool schema to verify. 6. Updates Cursor configuration atomically or uses Claude Code's native MCP config commands, preserving the original client scope. 7. Activates the profiles only after every selected broker and wrapper verifies. Equivalent definitions can share one protected profile, so the same MCP configured in more than one supported scope does not need duplicate broker processes. If a step fails, setup compensates in reverse: it restores client changes, removes services it installed, and revokes the provisional enrollment. Client files are not changed until all selected profile brokers are healthy. ## Profile isolation and the secret boundary Each protected profile has its own local configuration, credential, loopback endpoint, and broker process. The broker starts the real MCP server with the environment frozen during setup; it does not inherit a fresh ambient environment every time it launches. The frozen baseline includes ordinary runtime essentials such as `HOME`, `PATH`, temporary-directory, locale, and certificate-path variables, plus the selected MCP's environment. All `FLOWIFAI_*` variables and setup, enrollment, broker, and server-to-server secret names are stripped from the child MCP environment. The per-profile broker credential is separate and is never passed to the MCP child. This is useful process and configuration isolation, but it is **not** a container, VM, filesystem sandbox, or network firewall. An agent that also has direct credentials, shell access, or an ungated tool path can bypass flowifai. Read the [security model](/docs/security-model/) before protecting production capabilities. ## Status and service diagnostics List protected profiles, inspect the latest setup run, or name a run explicitly: ```sh flowifai profiles list flowifai setup status flowifai setup status RUN_ID flowifai setup logs RUN_ID ``` Setup status prints the durable run state, its profiles and activation state, the exact rollback deadline, and the safe next action. Setup logs prints the run's sanitized lifecycle timeline. The private JSONL source records only run ID, state, outcome, and time; it is mode `0600` and rotates at 1 MiB. Check one profile or all profiles: ```sh flowifai status --profile PROFILE_ID flowifai status --all flowifai doctor --profile PROFILE_ID --strict flowifai doctor --all --strict ``` Inspect or operate the separate profile service: ```sh flowifai broker status --profile PROFILE_ID flowifai broker logs --profile PROFILE_ID flowifai broker reload --profile PROFILE_ID flowifai broker stop --profile PROFILE_ID flowifai broker start --profile PROFILE_ID ``` Stopping or uninstalling a service is an operational action. It does not restore a client configuration or revoke the backend profile, so it is not a rollback. ## Rollback and interrupted-run recovery A successful setup prints its run ID and retains the exact client preimages and inverse operations for 30 days. During that window, roll back the entire setup run: ```sh flowifai setup rollback RUN_ID ``` Rollback requires browser authorization. It revokes the run's backend profiles before disabling local profiles, restoring the original Cursor and Claude Code configuration, and removing the run's broker services. It is resumable: if a rollback step fails, correct the underlying problem and run the same command again. Exact restoration is refused after the printed deadline because the retained client preimages are no longer safe to depend on. Use `flowifai setup status RUN_ID` before recovery rather than guessing which effects completed. If setup committed on the backend but was interrupted before publishing local activation, status directs you to: ```sh flowifai setup resume RUN_ID ``` Resume is intentionally fail-closed. It idempotently finishes `backend_committing` and `activation_pending`. For an interruption at `authorized`, it reconciles the exact journaled enrollment attempt and then performs a revoke-first rollback. For an already durable `enrolled` state, it continues that rollback. It never replays uncertain MCP inspection or client edits; other interrupted states require status-guided recovery. ## Remove one profile To permanently decommission one profile without affecting its siblings: ```sh flowifai profiles remove PROFILE_ID ``` Removal first verifies that every mapped client entry is still the exact flowifai wrapper for that profile. It then requires fresh browser authorization bound to the profile's setup run, revokes only that backend profile, disables only its local activation, removes its exact wrapper entries, stops and uninstalls its broker service, and deletes its local profile state. Sibling profiles remain active. This is decommissioning, not exact restoration: it removes the flowifai wrapper entry but does not reconstruct the original direct MCP definition. Use whole-run `flowifai setup rollback RUN_ID` within the 30-day window when you need the retained original definitions restored. A client entry changed since setup causes removal to stop rather than overwrite the change. ## Create-only profile changes Setup creates protected profiles; it does not edit a profile's MCP launch, frozen environment, or credential in place. If the underlying MCP command, arguments, working directory, or credentials change, treat that as a new setup operation rather than assuming a running profile was updated. ## Accessible terminal behavior The default interactive MCP picker uses keyboard navigation and cursor repainting. Run `flowifai setup --accessible`, or set `FLOWIFAI_ACCESSIBLE=1`, to replace it with numbered, line-oriented selection and confirmation prompts without ANSI styling or cursor control. Redirected input/output and `TERM=dumb` also receive stable plain output. All progress after selection is append-only, every status has a text label, and no meaning depends on color alone. This mode is designed for screen readers, dictation, terminal logs, and copied text; it is not a claim of certification with every terminal and assistive-technology combination. `--all --yes` can remove the selection and confirmation prompts, but browser authentication remains interactive. ## Manual or unsupported clients For a client or transport not listed above, keep using the explicit enrollment path: ```sh flowifai connect --backend https://app.staging.flowif.ai --name my-agent --pairing-token-stdin -- /path/to/mcp-server --flag value flowifai broker install-service flowifai broker start ``` Then print a wrapper path or supported manual client configuration: ```sh flowifai wrapper-path flowifai client-config claude --print ``` The manual singleton flow remains supported, but it does not provide the setup wizard's multi-profile orchestration. --- Source: https://flowif.ai/docs/quickstart/ Markdown: https://flowif.ai/docs/quickstart.md # Quickstart: your first MCP approval flowifai sits between your AI agent and local MCP tools, asks a human before high-impact calls run, and leaves the real service credentials on your machine. The setup wizard can discover and protect several existing Claude Code or Cursor MCP servers in one run. Commands on this page target `https://app.staging.flowif.ai` while the production application remains behind its launch gate. ## 1. Install the tools Install the CLI, broker, and wrapper on the machine that runs your MCP tools: ```sh curl -fsSL https://flowif.ai/install.sh | sh ``` Full installer details, update commands, and troubleshooting live in the [install guide](/docs/install/). ## 2. Preview discovery Run this from the project whose project-scoped MCP entries you want to discover: ```sh flowifai setup --dry-run ``` Dry run reads supported Claude Code and Cursor configuration and prints a redacted plan. It does not run an MCP server, open a browser, make a network request, or write a file. ## 3. Protect your MCP servers Start the wizard: ```sh flowifai setup ``` Choose numbers, a comma-separated list, a range such as `1,3-5`, or `all`. Setup inspects the selected servers, opens browser authentication, creates a separate protected profile process for each distinct MCP, verifies every broker, and only then updates the client entries. See [one-shot MCP setup](/docs/setup/) for supported scopes, the environment boundary, accessible terminal behavior, and recovery. ## 4. Verify the setup The setup result prints profile IDs. Verify one or all of them: ```sh flowifai status --all flowifai doctor --all --strict ``` Doctor exits nonzero if a selected profile is unhealthy. It does not prove end-to-end approval execution by itself. ## 5. Gate a tool and approve it Open **Policies**, allow safe tools, and gate risky ones with a policy rule (see the [policy reference](/docs/policy-reference/)). Gated calls appear in the inbox and approval notifications. When a gated call fires, the agent's tool call pauses as pending, you approve or deny it in the console, and the call completes on retry. In supported MCP clients the approval link is delivered protocol-natively, right in the client (see [protocol-native approvals](/docs/protocol-native-approvals/)). ## Common first-run questions ### What if my client is not supported by setup? The wizard currently modifies Claude Code user, project, and local scopes, and Cursor user and project scopes. Other clients are independently gated. Follow the [manual setup path](/docs/setup/#manual-or-unsupported-clients) until your client and scope are explicitly listed as supported. ### Do my MCP server's credentials go to the flowifai cloud? No. Credentials configured in your local MCP server stay on the tools host with the broker. The cloud backend stores policy, approval decisions, and audit records — never your downstream service credentials. Details are in the [security model](/docs/security-model/). --- Source: https://flowif.ai/docs/core-concepts/ Markdown: https://flowif.ai/docs/core-concepts.md # Core concepts: human in the loop MCP approvals flowifai is a human in the loop MCP approval layer: it intercepts an agent's MCP tool calls, adjudicates each one against your policy, and pauses high-impact calls until a human approves them. This page explains the moving parts and the guarantees behind them. ## The three processes - **`flowifai` (CLI)** — the operator tool. It enrolls agents, manages the broker service, prints client config, and manages policies and approvals. - **`flowifai-broker`** — the trusted local process on the tools host. It owns the enrollment credential, launches the real MCP server, asks the backend to adjudicate every call, and performs the actual tool execution. - **`flowifai-wrapper`** — the untrusted stdio shim your MCP client (Claude Code, VS Code, Cursor, …) launches instead of the real server. It presents the wrapped server's tools to the agent and forwards every call to the broker over loopback. It holds no flowifai credential, by design: bypassing it just reaches the broker, which still gates. ## The credential boundary Credentials configured in your local MCP server — API keys, service tokens — stay in the local tools sandbox with that server. The flowifai enrollment credential is separate: the broker uses it to authenticate *outbound* calls to the backend. The cloud backend is an approval authority, never a credential holder. It stores policy, approval tickets, audit records, and registered tool metadata. It has no downstream credential columns by construction. The backend also never initiates a connection into your machine — the model is pull-only: the broker asks the backend for decisions through outbound requests, and execution happens locally. One honest caveat: tool arguments and execution results are sent to the backend for policy, approval, and audit. Any value embedded in them — including an accidental secret — may be stored. See the [security model](/docs/security-model/). ## Adjudication: default-deny policy Every tool call the broker receives is adjudicated against your org's policy rules. A rule matches on the tool name (exact, or `*` for any tool) plus an optional CEL predicate over the call's arguments. Among matching rules, the most restrictive effect wins (`deny` > `needs_approval` > `allow`), and if no rule matches, the call is denied. The engine fails closed: unparseable arguments and predicate evaluation errors are denials, never silent allows. Full semantics in the [policy reference](/docs/policy-reference/). ## The approval ticket lifecycle A call whose decision is `needs_approval` becomes an approval ticket: 1. **pending** — the agent's tool call returns a pending result carrying an `approval_ref`. The human sees the request in the console inbox (and, in supported clients, gets the approval link delivered [protocol-natively](/docs/protocol-native-approvals/)). 2. **approved** or **denied** — an eligible human decides in the console. Approving requires a user identity distinct from the broker's machine identity, so an agent can never approve its own call. Policies can require n-of-m approvals across approver groups; one eligible deny vetoes. 3. **executing → executed** — when the agent retries with the `approval_ref`, the broker claims the ticket and runs the real tool. The result returns to the agent. 4. Terminal failures — **expired** (nobody decided in time, or approved-but-unexecuted lapsed), **revoked**, or **execution_failed**. Terminal states never silently re-arm: re-running the action requires a fresh call and a fresh human approval. ## Two-phase execution: exactly once Approved actions execute through a two-phase claim/complete protocol. The backend performs the atomic `approved → executing → executed` transition with a claimant token and a lease, so concurrent retries of the same ticket yield exactly one execution. If the broker crashes between invoking the tool and completing the ticket, the ticket becomes `execution_failed` and any re-run needs a new approval — the failure is reported honestly, never silently retried. ## Schema drift blocks calls At enrollment, `flowifai connect` inspects the MCP server and registers its tool schema. If the server's live schema later drifts from the registered one, the broker refuses all calls to it (fail closed) until you re-run the app-generated `flowifai connect` command. Policy rules that reference arguments of a changed schema are disabled until re-confirmed, so a renamed field can never quietly widen an allow rule. ## Audit Adjudications, approvals, denials, execution outcomes, and lifecycle events are written to an append-only audit log (updates and deletes are rejected at the database level). Security-critical transitions write their audit row in the same transaction as the state change, so a committed execution can never lack its audit record. ## What flowifai is not flowifai is a cooperative approval gate, not a sandbox. It gates the MCP path routed through the wrapper and broker. If an agent also holds direct API keys, shell access, or another ungated tool path, those paths remain outside flowifai's control. The [security model](/docs/security-model/) spells out the boundary. --- Source: https://flowif.ai/docs/protocol-native-approvals/ Markdown: https://flowif.ai/docs/protocol-native-approvals.md # Protocol-native approvals: the AI agent approval workflow in your MCP client flowifai's approval gate is server-side: a gated tool call pauses until an eligible human approves it in the console. Protocol-native approvals make that AI agent approval workflow feel native in the MCP client itself — the approval link is delivered over the MCP wire, the human approves in the console, and the client completes the call. No copy-pasting references, no polling by hand. ## What you see, step by step In a supported client (say, Claude Code): 1. The agent calls a gated tool. Instead of a plain "pending" text result, flowifai answers with a protocol-level URL elicitation carrying the approval deep link. 2. The client surfaces it to you: an approval is required, with a link into the flowifai console inbox for exactly this request. 3. A human opens the link and approves (or denies) in the console — same console, same policy checks, same audit trail as always. 4. flowifai notifies the client that the elicitation is complete; the client retries the tool call, the broker executes the approved action exactly once, and the agent gets the real result. If the request is denied, expires, or is revoked, the retry returns that outcome instead — the agent is never left hanging. > **How completion reaches your client.** flowifai emits a spec-standard > `notifications/elicitation/complete` when the decision lands, and also answers a > lightweight status poll, so the retry that finishes the call is driven by whichever your > client supports. In current clients the loop converges on the client's own retry after > approval; the push notification is there for clients that consume it. Either way you never > poll by hand, and the decision is always made server-side — the client never approves. ## Client support matrix | MCP client | Protocol-native approval loop | | --- | --- | | Claude Code ≥ 2.1.76 | Full loop: approval link surfaced in-client, call completes after approval | | VS Code ≥ 1.107 | Full loop: approval link surfaced in-client, call completes after approval | | Cursor | Not yet — pending text + manual retry | | Claude Desktop | Not yet — pending text + manual retry | | Windsurf | Not yet — pending text + manual retry | Support is verified per client family, not assumed from capability flags. The allowlist is config-extendable (`FLOWIFAI_ELICITATION_CLIENTS`) as more clients ship support. ## Graceful degradation Clients outside the matrix lose nothing they had before. On a gated call they receive today's pending text: the approval reference plus instructions to call the same tool again with the same arguments and the `__approval_ref` parameter set. A human approves in the console exactly as in the full loop; the agent (or you) retries and the call completes. The gate, the policy, and the audit trail are identical — only the delivery of the approval link is less integrated. flowifai never blocks on any client's adoption: the protocol-native path activates only when a session negotiates the required MCP protocol revision *and* the client is on the verified allowlist. Every other session keeps the baseline behavior unchanged. ## Delivery, not authorization The elicitation dialog is never the security boundary. What the client "accepts" is consent to open a URL — nothing more. The approval decision itself: - is made in the flowifai console by an eligible approver, authenticated as a human user distinct from the agent's machine identity; - is checked against your policy (n-of-m approvals, approver groups, one-deny veto); - is recorded in the append-only audit log, in the same transaction as the state change. No MCP client response — accept, decline, or anything a compromised agent might forge — can authorize execution. A client that mishandles the elicitation can only make the approval link less convenient to reach; it cannot approve anything. ## About the spec's "same user" expectation The MCP elicitation spec expects the user completing an elicitation to be the user interacting with the client. flowifai deliberately reconciles this with its separation-of-duty model: the person approving is often *not* the person (or agent) that initiated the call — that is the product, not a violation. The reconciliation holds because the URL itself grants nothing: it points at an org-gated console page, any org member may view it, only eligible approvers may decide, and every decision is server-side and audited. Delivering the link to the initiating user is a convenience; authorization never travels through the client. ## Spec status This behavior tracks the MCP specification revision 2025-11-25 (URL-mode elicitation), which is a pre-final revision — a follow-up spec revision is expected, and URL elicitation carries forward in it. flowifai's promised invariant is the degradation contract above: supported clients get the integrated loop, everything else keeps the plain pending-text behavior, and the server-side gate is identical in both. ## Related - [Core concepts](/docs/core-concepts/) — the ticket lifecycle behind the loop - [Security model](/docs/security-model/) — why the gate lives server-side - [Quickstart](/docs/quickstart/) — set up your first gated call --- Source: https://flowif.ai/docs/policy-reference/ Markdown: https://flowif.ai/docs/policy-reference.md # Policy reference (CEL) flowifai adjudicates every tool call your broker forwards against your org's policy rules. This page is the exact contract: what a rule is, how matching works, which effect wins, and how the engine fails. ## What a rule is A policy rule has: | Field | Meaning | | --- | --- | | Tool pattern | The exact tool name to match, or `*` for any tool. | | Argument predicate | Optional [CEL](https://cel.dev) expression over the call's arguments. Empty means "always matches". | | Effect | `allow`, `needs_approval`, or `deny`. | | Enabled | Disabled rules never match. | | Approval requirement | For `needs_approval` rules only: how many approvals (n-of-m), which approver groups are eligible, and whether approvals must span at least two groups. | Rules are org-level. Tool names come from the schemas your registered MCP servers expose — the console's policy view shows which registered server provides each tool, so you can scope a rule to a server's tools by naming them exactly. ## Matching A rule matches a call when **both** hold: 1. its tool pattern matches the called tool (exact name, or `*`), and 2. its argument predicate — if present — evaluates to `true`. Predicates are CEL expressions evaluated with two variables: - `args` — the call's arguments as a map (`map`), decoded from the tool call's JSON arguments object; - `tool` — the called tool's name as a string. Examples: ```text has(args.query) && type(args.query)==string && (args.query.contains("DROP")||args.query.contains("DELETE")||args.query.contains("TRUNCATE")) ``` ```text has(args.amount) && type(args.amount)==double && args.amount > 0.0 ``` ```text has(args.path) && has(args.recursive) && args.recursive == true ``` Predicates are compiled and validated when you save the rule; a rule with a predicate that does not compile is rejected. ## Most restrictive wins, default deny All enabled rules are evaluated against the call. Among the rules that match, the most restrictive effect wins: ```text deny > needs_approval > allow ``` If **no** rule matches, the decision is **deny**. There is no implicit allow anywhere: you must write an `allow` rule for the tools you want to pass ungated. When the winning decision is `needs_approval` and several `needs_approval` rules matched, the approval requirement is the strictest merge across them: the maximum approvals-required count, the union of eligible approver groups, and cross-group required if any matching rule requires it. The matched rules are surfaced to approvers as the "why gated" provenance on the ticket. ## Fail-closed behavior The engine treats every abnormal condition as a denial: - **Unparseable arguments** → deny. - **Predicate evaluation error** (for example, a predicate that touches a wrong-typed field) → the whole call is denied, even if another rule would have allowed it. - **Unknown effect value** → treated as deny. Because an erroring predicate denies the whole call, write broad (`*`-pattern) predicates **type-guarded** so they can only ever evaluate to true or false: ```text has(args.command) && type(args.command)==string && args.command.contains("deploy") ``` The `has(...)` and `type(...)` guards ensure the predicate never errors regardless of the argument shape a tool sends. ## Policy templates The console ships a curated catalog of commit-point templates (destructive SQL, payment/charge, outbound send, recursive filesystem delete, deploy/migrate/apply, git force-push). Every template uses tool pattern `*` with a type-guarded predicate and effect `needs_approval`. Templates key on *guessed* argument field names — real MCP tools use arbitrary schemas — so treat an applied template as a starting point and verify it matches your tool's actual fields (for example, your DB tool may call the query field `sql` or `statement` instead of `query`). ## Schema drift and stale rules Enrollment registers your MCP server's tool schema. If the live schema drifts from the registered one, the broker blocks all calls to that server until you re-run the app-generated `flowifai connect` command. Additionally, argument-level rules that reference a changed schema are **disabled** (fail closed) until re-confirmed — a renamed or re-typed field can never quietly turn a gate into a pass. ## Who can edit policy Policy create/delete requires a policy-admin user, which is a separate capability from approval eligibility. A single approver therefore cannot rewrite policy to bypass n-of-m or separation-of-duty requirements. ## Managing rules Manage rules in the console under **Policies**, or from the CLI: ```sh flowifai policy list flowifai policy add --name "gate deploys" --tool "*" \ --predicate 'has(args.command) && type(args.command)==string && args.command.contains("deploy")' \ --effect needs_approval flowifai policy delete ``` Rule changes are recorded in the audit log like every other security-relevant event. --- Source: https://flowif.ai/docs/troubleshooting/ Markdown: https://flowif.ai/docs/troubleshooting.md # Troubleshooting Start with the doctor. Then work through the specific failure below. ## First move: `flowifai doctor --strict` ```sh flowifai doctor --strict ``` Doctor runs a check series and prints one line per check with a recovery hint on anything unhealthy: | Check | What it verifies | | --- | --- | | `local_version` | The installed CLI version. | | `install_layout` | `~/.flowifai` home and install directory are intact. | | `local_config` | `~/.flowifai/config.json` exists with the values `connect` wrote. | | `broker_bind` | The configured broker listen address. | | `wrapper_path` | The installed `flowifai-wrapper` binary is present. | | `backend` | The backend URL answers its health endpoint. | | `registration` | Your server is registered under the configured name and credential. | | `broker` | The local broker answers on its URL. | With `--strict`, doctor exits nonzero when any check is warn/fail — useful in scripts. Add `--json` for machine-readable output, and `--check-update` to also verify the signed release channel. A green doctor does not by itself prove end-to-end approval execution; it proves install, backend, and broker health. For setup-managed profiles, diagnose one profile or all of them: ```sh flowifai status --profile PROFILE_ID flowifai doctor --profile PROFILE_ID --strict flowifai doctor --all --strict ``` Use `flowifai broker logs --profile PROFILE_ID` for that profile's service log. For an interrupted onboarding run, inspect the journal rather than rerunning setup blindly: ```sh flowifai setup status flowifai setup status RUN_ID flowifai setup logs RUN_ID ``` Status without a run ID selects the latest run. It prints the durable state, exact rollback deadline, and safe next action. Use `flowifai setup resume RUN_ID` when status directs you to it: commit-boundary states finish activation, while `authorized` or `enrolled` interruptions reconcile enrollment as needed and perform a revoke-first safe rollback. Other earlier uncertain states are refused. Setup logs prints a linear, sanitized timeline for the named run. Its private local JSONL file contains only run ID, lifecycle state, outcome, and time — no MCP arguments, environment values, raw client configuration, or credentials. The file is mode `0600` and rotates at 1 MiB. ## Restore client configuration after setup For 30 days after a successful setup, restore the entire run: ```sh flowifai setup rollback RUN_ID ``` Rollback opens browser authorization, revokes backend profiles first, disables local profiles, restores the exact retained Cursor and Claude Code configuration, and removes the run's broker services. If it is interrupted, fix the reported problem and run the same rollback command again. After the exact deadline shown by `flowifai setup status RUN_ID`, client restoration is refused. To decommission only one profile without restoring its original direct client definition: ```sh flowifai profiles remove PROFILE_ID ``` Profile removal requires browser authorization, revokes that backend profile before local changes, and preserves sibling profiles. It only removes exact matching flowifai wrapper entries. If a mapped client entry has changed, removal refuses to overwrite it; resolve the conflict and retry. ## Setup discovery did not find an MCP - Run setup from the relevant project directory. Project and local scopes are resolved against the current directory. - Use `flowifai setup --dry-run` to see every discovered result and the reason an entry is unavailable. - Only local stdio definitions are supported. Remote HTTP, SSE, WebSocket, missing commands, dynamic inputs, and ambiguous definitions are listed but not converted. - Setup currently reads Claude Code user, project, and local scopes and Cursor user and project scopes. Other clients are independently gated and are not modified. Dry run is safe for diagnosis: it does not execute MCP servers, open a browser, make network requests, or change files. ## A setup-managed profile broker will not start ```sh flowifai broker status --profile PROFILE_ID flowifai broker logs --profile PROFILE_ID flowifai doctor --profile PROFILE_ID --strict ``` The profile's MCP command, arguments, working directory, and environment are frozen at setup time. Editing your shell or original client entry later does not update the profile in place. Run a new setup operation for a changed launch or credential. Do not expose the profile's ephemeral loopback endpoint. Its runtime identity prevents stale routing after restart; it is not hostile-caller authentication. ## Pairing fails with 401 `flowifai connect` redeems a short-lived pairing token. The backend's 401 response is deliberately uniform — it does not say whether the token was expired, already used, or simply wrong, so a failed redeem can never be used to probe token state. The CLI adds the recovery hint: > pairing tokens are single-use and expire after 10 minutes — mint a new connect > command in the app (Agents → your agent → New connect command) That is the whole recovery: open the app, mint a fresh connect command for the agent, and run it within 10 minutes. If pairing commands are disabled for your org, the app shows an enrollment credential and a `--credential-stdin` fallback command instead. ## Missing or lost S2S credential The durable broker credential is created during pairing and stored only in `~/.flowifai/config.json`. If it is lost (deleted config, new machine), rotate/mint a replacement credential in the agent's detail page and use the `--credential-stdin` fallback. Credentials are shown once — paste them only when the CLI prompts. ## Broker will not start, or wrapper cannot reach it - Inspect startup failures: `flowifai broker logs`. - Same host: run `flowifai broker start` and point the wrapper at `FLOWIFAI_BROKER_URL=http://localhost:8090` (the broker binds `127.0.0.1:8090` by default). - Split host: the wrapper-facing broker endpoints are unauthenticated by design and must never be exposed to a LAN or VPN directly. Start an SSH tunnel (or equivalent authenticated encrypted loopback tunnel) first, then point `FLOWIFAI_BROKER_URL` at the local tunnel endpoint. - New API keys or PATH entries for your MCP server: rerun `flowifai broker install-service` (it re-snapshots the shell environment), then restart the broker. ## Schema drift: every call is refused If the wrapped MCP server's live tool schema no longer matches what was registered at enrollment, the broker fails closed and refuses all calls to it. Re-run the app-generated `flowifai connect` command to re-inspect and re-register, then: ```sh flowifai broker stop && flowifai broker start ``` so the startup drift checks re-run. Note that argument-level policy rules referencing a changed schema are disabled until re-confirmed — review them in **Policies** after a re-register. ## A gated call seems stuck pending - Check the console inbox: the ticket may simply be awaiting a human decision. - If it was approved, the agent must retry the same tool with the same arguments and the `__approval_ref` parameter set to the reference from the pending response. Supported clients do this automatically — see [protocol-native approvals](/docs/protocol-native-approvals/). - If the broker restarted while a call was pending, the stored arguments for replay are gone (they are held in memory, loss-tolerant by design); the retry will ask you to re-request, which creates a fresh ticket and a fresh approval. - Tickets that expire, are revoked, or fail during execution are terminal: re-running the action requires a new call and a new approval. That is fail-closed behavior, not a bug. ## Command not found Add `~/.flowifai/bin` to `PATH`, or use the absolute path. GUI-launched MCP clients often do not inherit your shell PATH — print the exact config with: ```sh flowifai client-config claude --print flowifai wrapper-path ``` ## Install and update issues - **Stale install lock**: remove `$FLOWIFAI_HOME/.install.lock` only after confirming no install or update is running. - **Unsupported platform**: Linux and macOS on amd64 or arm64 are supported. - **Release channel diagnostics**: `flowifai doctor --json --check-update` verifies the signed manifest and the asset selected for your platform. Still stuck? [Contact us](/contact/) — include your `flowifai doctor --json` output (it contains no credentials). --- Source: https://flowif.ai/docs/security-model/ Markdown: https://flowif.ai/docs/security-model.md # Security model flowifai is an approval layer for cooperative MCP tool paths. It keeps downstream service credentials local while the backend handles policy, approval, audit, and registered tool metadata. This page states the threat model plainly: what is enforced, where, and where the limits are. ## The adversary is the agent The threat flowifai is built for is the agent itself — prompt injection, confused deputies, and plain mistakes — not a hostile human operator with root on the box. The design goal is that an injected or misbehaving agent cannot execute a gated action without a human decision, cannot approve its own calls, and cannot erase the record of what it did. ## Credential boundary Credentials configured in your local MCP server — API keys, service tokens — stay in the local tools sandbox with that server. The flowifai enrollment credential is separate: the broker uses it to authenticate outbound calls to the backend. | Local tools host | flowifai cloud | | --- | --- | | MCP server credentials, real tool execution, broker service, wrapper-facing broker endpoint, local flowifai config | Policy, approval tickets, audit records, registered tool metadata, setup lifecycle events, billing/auth state | The backend's store has no downstream credential columns by construction — the cloud is an approval authority, never a credential holder. ## What the backend can receive The backend may receive and store registered tool names, descriptions, input schemas, arguments needed for policy and approval, approval decisions, audit metadata, lifecycle events, and execution results. Any value included in tool arguments or execution results — including accidental secrets — may therefore be sent to and stored by the backend. Treat arguments and results as sensitive data. ## Broker and wrapper The broker is the local trust anchor. It owns the enrollment credential, launches the real MCP server, and never trusts client-asserted approval — it resolves every ticket against the backend over its authenticated server-to-server channel. The wrapper is an untrusted stdio shim for the agent and owns no flowifai credential. Bypassing the wrapper just reaches the broker, which still gates. The broker's wrapper-facing endpoints are not authenticated, so same-host setups should keep the broker on loopback (the default is `127.0.0.1:8090`) and split-host setups should use an SSH or equivalent authenticated encrypted loopback tunnel. ### Setup-managed profile processes The setup wizard gives each distinct protected MCP launch a private profile, credential, loopback endpoint, and broker process. A wrapper names its profile, reads that profile's current runtime identity, and reconnects if the broker restarts. The broker checks that the profile is active on each tool call. The real MCP process receives the environment frozen for that profile during setup, not the broker's current ambient environment. flowifai control variables and setup, enrollment, broker, and server-to-server secret names are removed, and the broker's per-profile credential is never passed to the child MCP process. These boundaries reduce accidental cross-profile sharing. They are process and configuration isolation only — not container, VM, filesystem, or network isolation. The wrapper-facing loopback endpoint uses profile runtime identity for routing and restart detection; it is not authentication against a hostile same-host caller. ## Pull-only cloud model The backend never initiates a connection into your machine. The broker asks the backend for policy and approval decisions through outbound requests, and the local broker performs the actual tool execution through the MCP server you configured. There is no daemon the cloud can push into. ## Approval integrity Approving or denying requires a human user identity distinct from the broker's machine identity — an agent holding the broker's credential cannot self-approve. Policies can require n-of-m approvals across approver groups with cross-group separation of duty; one eligible deny vetoes. Policy authorship is a separate capability from approval eligibility, so a lone approver cannot rewrite policy to bypass the gate. Approved actions execute exactly once per ticket via a two-phase claim/complete protocol; a broker crash mid-execution resolves to `execution_failed` and a fresh human approval, never a silent re-run. The [protocol-native approval delivery](/docs/protocol-native-approvals/) in MCP clients is delivery only: no client response can authorize execution — the decision is always server-side, policy-checked, and audited. ## Fail closed everywhere Policy is default-deny: no matching rule means deny, unparseable arguments mean deny, and a predicate evaluation error denies the whole call. An unregistered or schema-drifted server refuses all calls. Approved-but-unexecuted tickets expire. Execution failures are terminal and never auto re-arm. ## Audit and non-repudiation Adjudications, approvals, denials, execution outcomes, drift events, auth failures, policy changes, and lifecycle transitions are written to an append-only audit log — updates and deletes are rejected at the database level. Security-critical transitions (approve, deny, execution complete, execution fail) write their audit row in the same transaction as the state change, so a committed execution can never lack its audit record. Correlation IDs are broker-minted, so audit attribution is not agent-forgeable. ## What flowifai is not flowifai is a **cooperative gate, not a sandbox**. It is not a VM sandbox, EDR product, network firewall, or guarantee that a compromised agent cannot act through some other path. It gates the MCP path you route through the wrapper and broker. If an agent also has direct API keys, shell access, or another ungated tool path, those paths remain outside flowifai control. Keeping the agent away from credentials and ungated paths is a *deployment* property: run the agent and the tools in separate sandboxes, do not co-mount secrets into the agent's environment, and treat the broker as the tools sandbox's only ingress. flowifai supports and documents this topology but cannot enforce it from code. ## Retention and deletion posture Audit and approval records exist so operators can review what happened. Public launch plans keep pricing and billing simple while retention controls mature; if your evaluation needs a specific deletion or retention commitment, [contact the team](/contact/) before routing sensitive production workflows.