Approval Flow
When a policy rule has action: human_approval, the tool call enters an approval queue. The agent waits. A human (or supervisor) resolves it. The tool call proceeds or is rejected.
How it works
Agent calls filesystem.write_file
│
├── Policy: human_approval
│
├── Check 1: Temporal grant active?
│ yes → bypass approval, proceed
│
├── Check 2: flux7-memory auto-approve? (3+ past approvals)
│ yes → proceed, traced as supervisor:mem7
│
└── Submit to approval queue
│
├── MCP mode: routed per approval.channel (TTY prompt or queue)
├── HTTP mode: always the queue — blocks until resolved
│
└── Human/supervisor resolves
├── approved → tool call executed, result returned
├── denied → 403 returned
└── timeout → 408 returned (default 5 min)
Routing: approval.channel
Version
Since v0.15.0. Older binaries silently ignore the key.
In MCP mode, approval.channel decides where a human_approval request goes:
| Channel | Behavior |
|---|---|
queue |
Always enqueue. For daemons and supervisor setups — a service has no terminal to prompt on. |
tty |
Require the interactive /dev/tty prompt. If no TTY is available, the call is denied (fail-closed), never silently queued. |
tty-fallback |
Try the TTY prompt, fall back to the queue. Default — matches the historical behavior. |
Without an explicit channel, routing depends on how the process was launched: a daemon started from a terminal keeps a usable /dev/tty and will prompt in a window nobody watches. Set channel: queue for any unattended deployment (systemd, container, supervisor loop).
The HTTP proxy path (POST /tool/{name}) always uses the queue regardless of this setting.
Resolving approvals
In Claude Code (MCP mode)
Claude Code shows a permission prompt inline. The developer says yes or no. This is the default for solo dev use.
Via MCP virtual tools
The approval.* virtual tools are operator-only: only a declared supervisor
agent (supervisor.supervisor_agents glob) may list or resolve approvals over
MCP. A regular agent cannot — otherwise it could approve its own pending
human_approval request and defeat the gate. With no supervisor configured,
resolve approvals via the CLI or HTTP API below.
approval.pending → list pending approvals (supervisor agents only)
approval.resolve {id, decision} → approve or deny (supervisor agents only)
Via CLI
# List pending
mesh pending
# Approve (prefix match)
mesh approve a1b2c3d4
# Deny
mesh deny a1b2c3d4
# Watch (live updates)
mesh watch
Via HTTP API
# List all approvals
curl http://localhost:9090/approvals
# Get details (includes recent traces and active grants)
curl http://localhost:9090/approvals/a1b2c3d4e5f6g7h8
# Approve
curl -X POST http://localhost:9090/approvals/a1b2c3d4/approve \
-H "Content-Type: application/json" \
-d '{"resolved_by":"user:marc","reasoning":"routine operation","confidence":0.95}'
# Deny
curl -X POST http://localhost:9090/approvals/a1b2c3d4/deny \
-d '{"resolved_by":"user:marc","reasoning":"unexpected target"}'
Prefix matching: a1b2c3d4 matches the full ID if the prefix is unique.
Temporal grants
Repeated approvals for the same tool pattern get tedious. Grants are like sudo — a temporary bypass:
For the next 30 minutes, all filesystem.write_* calls bypass the approval queue. Traced as grant:<id>.
MCP tools
HTTP API
# Create
curl -X POST http://localhost:9090/grants \
-d '{"agent":"claude","tools":"filesystem.*","duration":"30m"}'
# List
curl http://localhost:9090/grants
# Revoke
curl -X DELETE http://localhost:9090/grants/abc123
Grants only bypass human_approval
Tools marked deny remain blocked. A grant cannot override a deny rule — that requires a policy edit.
Timeouts
Unanswered approvals time out after 5 minutes (configurable):
Timed-out approvals are recorded in traces and written to flux7-memory (if configured) with status timeout.
Webhooks
Get notified when a new approval is pending:
flux7-mesh POSTs to this URL with the pending approval details. Useful for Slack/Teams alerts.
Callback URL
HTTP agents can provide a callback URL to receive the resolution:
curl -X POST http://localhost:9090/tool/gmail.send_email \
-H "Authorization: Bearer agent:my-bot" \
-H "X-Callback-URL: http://my-bot:8080/approval-callback" \
-d '{"params":{"to":"user@example.com","subject":"Hello"}}'
When the approval resolves, flux7-mesh POSTs the result to X-Callback-URL.
Supervisor mode
When supervisor.enabled: true, the approval tools (approval.resolve, approval.pending) are hidden from agents. Only an external supervisor can resolve approvals:
supervisor:
enabled: true
expose_content: false # redact params for the supervisor
supervisor_agents: # whitelist (glob) for cloud supervisors
- "supervisor-*"
In this mode, MCP tool calls block until the supervisor resolves. The agent waits transparently.
See Supervisor Protocol for the external resolver API.
Traces
Every approval is traced:
{
"tool": "filesystem.write_file",
"policy": "human_approval",
"approval_id": "a1b2c3d4e5f6g7h8",
"approval_status": "approved",
"approved_by": "user:marc",
"supervisor_reasoning": "routine write operation",
"supervisor_confidence": 0.95,
"latency_ms": 4200
}
Query approval history:
Decision persistence
When flux7-memory is configured, every approval resolution (approve, deny, timeout) is stored as a queryable fact. This enables:
- Auto-approve — routine patterns resolve without human intervention
- Audit trail — "who approved what, when, why" is queryable
- Cross-session memory — decisions survive process restarts
Next steps
- Memory Integration — auto-approve from past decisions
- CLI Tools — governing git, docker, terraform
- Deployment Modes — solo, team, cloud