Trust, Audit & Control
FlowDot is a Guardian Agents platform, the analyst-named category Gartner forecasts at 10-15% of the agentic AI market by 2030. Most agent platforms treat trust as an add-on: a dashboard widget that summarises yesterday's spend, a kill switch that only stops the surface it was pressed on, an audit trail that fabricates data when it can't see the truth. FlowDot ships verifiable human-control as a first-class property of every running session. This page is the user-facing reference for how that works and what it changes.
TL;DR
- Four-layer attribution on every LLM call: aggregator / routing provider / source provider / model. You always know exactly who handled your request.
- Real-time cost surfacing in the chat itself, not buried in a dashboard. Hover the
$indicator on any agent message to see tokens in / out, exact dollar cost, and your running conversation total. - One Trust Center at /observability consolidates every server-side audit source into a single user-scoped view.
- Cross-surface panic E-stop. Press it from a phone while at lunch. The Hub flips a sticky flag and every outbound endpoint returns
HTTP 423 Lockeduntil you clear it with your password. - Per-tool permission gate with five scopes that persist across surfaces. Approving a tool on the desktop applies the next time a voice agent on mobile reaches for it.
- Per-surface memory attribution. You decide which surface and mode is allowed to write to your Memories. Voice on mobile can be on; recipes from the CLI can be off; nothing writes implicitly.
- We refuse to fabricate audit data. When an agent call legitimately bypasses our Hub (local Ollama, OAuth-Codex through your own ChatGPT subscription), the Trust Center shows a banner explaining the gap instead of inventing rows.
Why This is Different
Verifiable human-control should not be a premium tier. It should be the floor. Here is the comparison the platform was built against. For the detailed comparison against each named competitor (n8n, Zapier, LangFlow, Claude Code, LangChain, AutoGen, CrewAI), see /docs/trust/comparison.
| Property | Typical agent platform | FlowDot |
|---|---|---|
| Provider visibility | Brand-only ("powered by GPT") | Four layers, every call: aggregator, routing provider, source provider, model. Visible in the chat as it happens. |
| Cost surfacing | Monthly dashboard, sometimes delayed | Per-message dollar amount in the chat. Running conversation total. Live token counters during streaming. |
| Emergency stop | Single-surface, soft (cancels the current request) | Cross-surface, sticky, password-confirmed clear. One press on any surface freezes every outbound action until you explicitly clear it. |
| Tool permissions | Pre-flight "approve once" prompt or no prompt at all | Five-scope gate at the per-tool level, persisted across every surface. The model proposes; you authorise. |
| Memory writes | Implicit, opaque, or off-by-default | Per-surface × per-mode attribution matrix the user owns. Default writes are gated server-side, not just hidden client-side. |
| Honesty about what isn't audited | Pretends every call is captured. Sometimes invents totals. | When a call legitimately bypasses the Hub, the Trust Center says so on a banner and links to the receipt path. |
| Prompt-injection defense | Often absent from documentation, frequently absent in code | Untrusted-content envelopes, signed inbound control frames, and provenance-aware approval prompts in the CLI runtime. |
Four-Layer Attribution
"Powered by AI" is not attribution. When you ask an agent on FlowDot to do something, the platform records and displays the entire chain of providers that fulfilled the call. This matters because the only way to evaluate a model's behaviour is to know which model actually ran.
| Layer | What it is | Example |
|---|---|---|
| 1. Aggregator | The user-facing brand you contracted with | FlowDot |
| 2. Routing provider | Who FlowDot routed the call through | redpill, bedrock, openrouter |
| 3. Source provider | The provider that owns the model | openai, anthropic, google |
| 4. Model | The specific model that ran | gpt-5, claude-haiku-4-5 |
Bring-Your-Own-Key calls have no aggregator and no routing layer: the chain collapses to source/model, which is exactly what you'd expect when the request goes directly from FlowDot to the provider account you own. Whatever path the call takes, the audit row records every layer it knows about. We do not paper over differences in routing by collapsing them.
Real-Time Cost & Tokens
Every agent message in the chat shows a small $ indicator next to the provider attribution line. Hover it for the per-message receipt:
- Tokens: exact input and output counts as reported by the provider
- Cost: dollar amount that was deducted from your balance, computed from current model pricing
- Conversation total: running sum across the entire chat so you can see cumulative drift
- Colour cue: grey under one cent, amber from one cent, orange above five cents. No flashing, no hectoring. Just signal.
The receipt survives a page reload because the provider, model, and underlying provider are persisted on every conversation message. You can come back to a chat tomorrow, scroll up, and verify which model said which thing. This is the same data the four-layer attribution chain uses.
For BYOK calls the cost field reads "billed to your provider account" rather than showing a fabricated number. We have the tokens but not the rate, so we won't pretend.
The Trust Center
The page at flowdot.ai/observability is the user-facing consolidation of every server-side audit source FlowDot maintains. One screen, one user, one timeline. No admin view. No team-level aggregation. Just yours.
| Region | What it shows |
|---|---|
| Boundary banner | Plain-language explanation of which call types bypass the Hub by design and therefore won't appear below. Links to the OAuth and local-LLM receipts. |
| KPI strip | Total spend, total tokens, total calls, voice-session count over the selected window. |
| Time series | Daily, weekly, or monthly stacks of aggregator credits, API key spend, and live voice sessions. |
| Surface tabs | All / Agent / Recipe / Goal / Workflow / Voice / API Keys. Filter the feed without losing context. |
| Breakdowns | Group by surface, model, provider, underlying provider, or tool. |
| Unified feed | Every event from every audit source in one chronological list. Click through for the full per-event drill-down. |
| CSV export | Take your own data wherever you need it. No portability tax. |
The Trust Center reads from the per-call USD ledger, the per-step recipe rollups, the per-node workflow audit, the live voice session table, and the programmatic API usage table. If FlowDot recorded it server-side, this is where you find it.
Panic E-Stop
The user story behind the panic button is concrete: you are away from your desk. You see something you don't like. An auto-replied email, an agent thread going off the rails, a recipe doing the wrong thing. You press one button on whatever surface you have. Within seconds every outbound action stops. The stop is sticky until you clear it.
How it works
The Hub is the chokepoint for every action that touches the outside world: email send, third-party toolkit invocation, workflow execution, LLM call, COMMS notification. A single sticky flag on the Hub politely refuses every side-effectful endpoint with HTTP 423 Locked until the user clears it. Local read-only navigation stays available; outbound state changes do not.
What that gives you
- Cross-surface by design. Press it from the web top bar, the native sidebar, the mobile drawer, the VR settings panel, the CLI, or the MCP
panic_stoptool. The Hub doesn't care which surface fired; it reacts the same way. - Works without daemons. Even if no local agent is reachable, the Hub gate still refuses outbound work. Panic is not dependent on a local process being online.
- Reach into local processes too. Online daemons receive the panic frame on their normal poll and abort active recipes, goals, relays, and pending commands without waiting for their next user action.
- Sticky until cleared. No auto-clear, no timeout. The system stays halted until you say otherwise.
- Password-confirmed clear. Clearing the panic requires recent password confirmation. An attacker with a stolen session cookie cannot quietly clear panic to hide their tracks.
- Noisy on press and clear. Every press and every clear fires a notification through every COMMS channel you have enabled, plus your account email. Silent denial-of-service becomes loud denial-of-service.
- Append-only audit. Every press and every clear writes a forensic row: surface, IP, user agent, stopped execution IDs, stopped agent sessions, drained command count. No DELETE path exists in the code.
- Per-user blast radius. No code path can panic another user's account. The button is yours, and only yours.
- Two-tap arming on every surface. First tap arms the control for three seconds; second tap fires. Quiet warning colour, no fire-engine red. Easy to find, hard to misfire.
The 423 contract
When panic is active, every gated endpoint returns:
HTTP/1.1 423 Locked
Content-Type: application/json
{
"error": "panic_active",
"message": "Your account has an active emergency stop. Outbound actions are blocked. Clear it from flowdot.ai/settings/panic.",
"panicked_at": "2026-05-12T18:23:11Z",
"panic_clear_url": "https://flowdot.ai/settings/panic"
}
423 was picked deliberately. RFC 4918 defines it as "the resource is currently locked" - a temporary, owner-clearable condition. That is exactly what panic is, and that semantic lets clients distinguish panic from real authentication or authorisation failures and present the right user experience.
Recipe and goal callers cannot press panic. The recipe runtime and the daemon goal runner stamp x-flowdot-mode on every Hub call, and the panic controller rejects presses carrying those modes. An adversarial vendor input that convinces an autonomous agent loop to invoke the panic tool cannot deny service to you, because only direct user-initiated callers are allowed to press.
Read more on the Panic Settings page (your canonical clear surface and audit log) and below in Per-Tool Permission Gate.
Per-Tool Permission Gate
Every consequential tool call goes through a permission prompt with five scopes. The model proposes; you authorise. No tool runs the first time without you saying so.
| Scope | Meaning | Best for |
|---|---|---|
| Once | Approve this single call only. Next call asks again. | One-off actions, exploring a new toolkit. |
| This session | Approve this tool for the rest of this chat / recipe run. | Iterative work where the agent will chain several calls to the same tool. |
| This tool forever | Approve this specific tool permanently. | Tools you've grown to trust: web search, file read, knowledge query. |
| This entire toolkit forever | Approve every tool in the parent toolkit. | Vendor toolkits you've vetted as a whole. |
| Deny | Refuse this call and tell the model. | Anything you don't want to authorise right now. |
Persisted approvals follow the user, not the surface. If you approve schwab:place_order on the desktop, the next time a voice agent on mobile reaches for the same tool, the gate already knows your answer. This is what makes a multi-surface platform usable without turning the permission prompt into a chore.
When a recipe needs human approval at a specific step rather than a specific tool, recipe authors use the gate step type. The same async hand-off path applies: a gate step can deliver its prompt to Telegram, you tap "Approve" between bites, the recipe continues. See Recipes for the step model.
Per-Surface Memory Attribution
FlowDot can persist facts about you across conversations: project context, preferences, references, feedback you've given. That is useful. It is also exactly the kind of feature that should not write implicitly. The matrix that controls writes is yours.
Every memory write is attributed to a combination of two values: the surface the call came from (web, CLI, native desktop, mobile) and the mode that drove the call (agent, recipe, goal, voice). You toggle each combination independently on the /memories page.
| Combination | Default | Why |
|---|---|---|
| Web agent, native agent, CLI agent, mobile agent | On | Conversational agent use is the canonical write path. The user is right there. |
| Voice | On | Voice is conversational; same reasoning. |
| Recipe (any surface) | Off | Recipes are autonomous. They should not back-fill your memory without an explicit opt-in. |
| Goals | Off | Long-running objectives are autonomous; same reasoning as recipes. |
| Email tool | Off | Inbound email is an untrusted-content surface. We default-deny memory writes from it. |
The toggle is enforced server-side, not just hidden client-side. When the toggle is off, the memory service early-returns before any model call and before any database write. There is no path where a memory gets written that contradicts your matrix.
Zero-Trust Agent Runtime
For agent platforms, "zero trust" is not a network primitive. It is the discipline of refusing to treat any input the model sees as authoritative, and refusing to act on the model's output without provenance-aware approval. The FlowDot CLI runtime enforces this in several specific places.
| Control | What it does |
|---|---|
| Untrusted-content envelopes | Web pages, search snippets, and fetched URL bodies are wrapped in a fixed boundary before being passed to the model with an explicit instruction that the enclosed text is data, not commands. Trusted domain does not mean trusted content. |
| Third-party metadata sanitisation | MCP server tool descriptions and toolkit tool prompts are sanitised before being injected into prompts. A malicious or compromised tool registry cannot reshape the agent's behaviour through descriptive prose. |
| Path-validated file inclusion | The @file prompt inclusion shortcut goes through the same path validation and file-read approval system as direct file reads. There is no separate, weaker read path. |
| Signed inbound control frames | The daemon verifies server-side cryptographic signatures on inbound SSE control frames and daemon command frames before acting on them. Bearer-token transport authenticity is not the only check. |
| Replay protection | Nonce tracking on the CLI rejects reused inbound signed frames. A replayed command does not become a fresh one. |
| HTTPS enforcement off loopback | The CLI rejects non-loopback http:// hub URLs at configuration time. Loopback is permitted for local development; arbitrary plaintext HTTP is not. |
| Structured argv execution where possible | Simple commands run through structured argv rather than shell-string parsing. Raw shell execution is reserved for cases that actually require it. |
| Provenance-aware approval prompts | When an action is proposed by the model after reading external content, the approval prompt surfaces that provenance instead of treating it like direct user intent. |
The CLI ships with a full Zero Trust audit document (flowdot-cli/ZERO_TRUST_AUDIT.md) that lists the controls, the threats they address, and the open hardening items by phase. Security work is published, not buried.
What We Refuse to Fabricate
Some agent calls legitimately do not touch the Hub. Two examples:
- Local Ollama. When you have Ollama running, the FlowDot desktop and CLI detect it and route opaque completions through it. The FlowDot server never sees the prompt body or the response. There is no row to write.
- OAuth-Codex. When you authorise your personal ChatGPT subscription via PKCE on the CLI or native desktop, the agent call goes directly from your device to your provider account. You are billed by your subscription, not by FlowDot. The Hub is not in the path.
The dishonest move would be to estimate or back-fill those calls so the Trust Center looks "complete." We refuse to do that. The page renders a permanent banner explaining which classes of call legitimately bypass the Hub, why, and where to look for receipts (the local log file on your machine, or your provider's own billing portal). Trust depends on the platform admitting what it does not know.
If we ever add opt-in self-reporting for OAuth-bypass usage, it will be a deliberate, documented Phase 2 with its own user-visible setting. We will not silently start writing rows that look like first-party receipts but are actually self-reports from a client we cannot independently verify.