AI agents are increasingly able to deploy code, modify infrastructure, query sensitive systems, and initiate financial operations. A conversational “Are you sure?” prompt is useful, but it is not independent authorization. The same agent that proposed the action may also control the confirmation flow.
I wanted an approval mechanism with four properties:
- A person approves one specific action.
- The approval expires quickly.
- It cannot be replayed for another tool call.
- The approval service does not need the agent's prompt or source code.
The resulting design is GoodRoom.verify, an MCP sidecar that pauses a configured high-risk action while an operator verifies with a passkey.
The approval flow
The agent calls an MCP tool with a human-readable summary, a SHA-256 hash of the canonical action, a risk level, and an audience identifying the protected tool.
{
"action_summary": "Deploy migration to production",
"action_hash": "sha256:...",
"risk_level": "CRITICAL",
"audience": "tool:db_migrate"
}
The gateway sends those fields to the approval API and receives separate approval and polling capabilities. The approval page displays the summary and requests WebAuthn user verification.
After successful verification, the API atomically consumes the challenge and issues a short-lived Ed25519 proof. The MCP gateway downloads the public JWKS and verifies the signature, issuer, audience, action hash, key ID, and expiry locally.
The protected operation should independently require that proof. Without that enforcement point, an approval UI is only advisory.
Why passkeys?
WebAuthn provides phishing-resistant user verification tied to the relying party. The private credential remains in the platform authenticator or hardware key. It also makes physical operator presence a clearer security boundary than another button controlled by the agent runtime.
Minimize what crosses the boundary
The approval service does not need the prompt, conversation, source code, or raw tool arguments. GoodRoom.verify receives a bounded display summary and an action hash.
Persistent audit records can retain the hash, result, proof identifier, and timestamps without retaining the summary.
What the proof must bind
A useful execution proof needs more than an “approved” boolean. The GoodRoom.verify design binds it to:
- the account and challenge;
- the canonical action hash;
- the protected tool audience;
- the risk level;
- a unique proof identifier;
- a short validity window.
The protected tool must reject a mismatched audience or action hash, an expired signature, an unknown signing key, and a reused proof identifier.
What this does not solve
This design does not determine whether an action is safe, sandbox the tool, or prevent a runtime from bypassing the approval tool. The agent runtime or tool wrapper must enforce proof verification, and it must hash the same canonical action that will execute.
That last point matters: if the reviewed payload and executed payload can differ, the approval is meaningless.
Where should enforcement live?
There are three plausible places:
- Runtime policy: easy to apply consistently, but only as strong as the runtime boundary.
- MCP server middleware: close to tool dispatch and reusable across tools, but bypassable if another path reaches the tool.
- The final tool or API: strongest binding to execution, but requires the deepest integration.
My current preference is defense in depth: runtime or MCP policy for usability, with final verification at the sensitive tool boundary whenever possible.
GoodRoom.verify is currently a private-beta MVP, not a finished security product. I am looking for feedback from developers building agents with sensitive tools, particularly on proof enforcement and action canonicalization.
See the architecture and request beta access.
What is the riskiest tool your agent can call today, and where do you enforce human approval?
Top comments (0)