[TECHNICAL DEEP-DIVES]

[

2/17/26

]

I Added Execution Governance to My Agent in 12 Minutes. Here's Exactly What Happened.

[Author]:

Amjad Fatmi

I want to show you something that isn't in any of our technical posts.

Not the threat model. Not the research papers. Not the attack class analysis. All of that exists and matters. But none of it shows you what it actually feels like to sit down, open a terminal, and add an execution boundary to an agent you already have running.

So that's what this post is. Twelve minutes. Start to finish. Real output at every step.

The Starting Point

You have a LangChain agent. It has tools. It's doing things in production. You've read enough to understand that "guardrails" and "observability" are not the same as an authorization boundary, and you want to add one.

This is where we start.

from langchain.agents import AgentExecutor
from langchain.tools import tool

@tool
def process_refund(customer_id: str, amount: float) -> str:
    """Process a customer refund."""
    # calls your payments API
    return payments_api.refund(customer_id, amount)

@tool  
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a customer."""
    return email_client.send(to, subject, body)

agent = AgentExecutor(agent=..., tools=[process_refund, send_email])
from langchain.agents import AgentExecutor
from langchain.tools import tool

@tool
def process_refund(customer_id: str, amount: float) -> str:
    """Process a customer refund."""
    # calls your payments API
    return payments_api.refund(customer_id, amount)

@tool  
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a customer."""
    return email_client.send(to, subject, body)

agent = AgentExecutor(agent=..., tools=[process_refund, send_email])
from langchain.agents import AgentExecutor
from langchain.tools import tool

@tool
def process_refund(customer_id: str, amount: float) -> str:
    """Process a customer refund."""
    # calls your payments API
    return payments_api.refund(customer_id, amount)

@tool  
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a customer."""
    return email_client.send(to, subject, body)

agent = AgentExecutor(agent=..., tools=[process_refund, send_email])

Two tools. Both effectful. Neither has any authorization layer between the model's decision and execution.

Minute 1–2: Install

git clone https://github.com/faramesh/faramesh-core.git
cd faramesh-core
pip install -e ".[cli]"
git clone https://github.com/faramesh/faramesh-core.git
cd faramesh-core
pip install -e ".[cli]"
git clone https://github.com/faramesh/faramesh-core.git
cd faramesh-core
pip install -e ".[cli]"

The [cli] flag pulls in rich for colored terminal output. Not required, but you'll want it. The difference between plain text output and the formatted CLI is the difference between squinting at logs and actually understanding what's happening.

Copy the example config:

cp
cp
cp

Open .env. For local development the defaults work. SQLite as the database, no external secrets manager yet. You can swap to PostgreSQL and Vault later. For now you want to see it work.

Minute 3: Start the Server

faramesh server start
faramesh server start
faramesh server start

That's it. The FastAPI server starts on localhost:8000. The web dashboard is at localhost:8000/ui. Open it. You'll see an empty actions table, a policy editor, and an approvals queue. All empty. Nothing has run yet.

The server is what every tool call will pass through before it executes. Not after. Before.

Minute 4–5: Write Your First Policy

This is the part that surprised me when I first used it. The policy is not code. It's a YAML file that lives next to your agent. It's the thing you check into git, review in pull requests, and point to when someone asks "what is this agent authorized to do?"

Create policies/support-agent.yaml:

rules:
  # Small refunds auto-approve
  - match:
      tool: process_refund
      amount_lte: 150
    allow: true
    description: "Small refunds auto-approved"

  # Mid-range refunds need a human
  - match:
      tool: process_refund
      amount_gt: 150
      amount_lte: 1000
    require_approval: true
    description: "Mid-range refunds require manager review"

  # Large refunds are blocked entirely
  - match:
      tool: process_refund
      amount_gt: 1000
    deny: true
    reason: "Refunds above $1000 require finance team authorization via separate workflow"

  # Emails to customers are allowed
  - match:
      tool: send_email
    allow: true
    description: "Customer emails auto-approved"

risk:
  rules:
    - name: large-financial-action
      when:
        tool: process_refund
        amount_gt: 500
      risk_level

rules:
  # Small refunds auto-approve
  - match:
      tool: process_refund
      amount_lte: 150
    allow: true
    description: "Small refunds auto-approved"

  # Mid-range refunds need a human
  - match:
      tool: process_refund
      amount_gt: 150
      amount_lte: 1000
    require_approval: true
    description: "Mid-range refunds require manager review"

  # Large refunds are blocked entirely
  - match:
      tool: process_refund
      amount_gt: 1000
    deny: true
    reason: "Refunds above $1000 require finance team authorization via separate workflow"

  # Emails to customers are allowed
  - match:
      tool: send_email
    allow: true
    description: "Customer emails auto-approved"

risk:
  rules:
    - name: large-financial-action
      when:
        tool: process_refund
        amount_gt: 500
      risk_level

rules:
  # Small refunds auto-approve
  - match:
      tool: process_refund
      amount_lte: 150
    allow: true
    description: "Small refunds auto-approved"

  # Mid-range refunds need a human
  - match:
      tool: process_refund
      amount_gt: 150
      amount_lte: 1000
    require_approval: true
    description: "Mid-range refunds require manager review"

  # Large refunds are blocked entirely
  - match:
      tool: process_refund
      amount_gt: 1000
    deny: true
    reason: "Refunds above $1000 require finance team authorization via separate workflow"

  # Emails to customers are allowed
  - match:
      tool: send_email
    allow: true
    description: "Customer emails auto-approved"

risk:
  rules:
    - name: large-financial-action
      when:
        tool: process_refund
        amount_gt: 500
      risk_level

Read this policy out loud. It says exactly what it means. Any engineer on your team, any security auditor, any compliance reviewer can read this file and understand exactly what your agent is authorized to do. Without reading your code.

The risk rule at the bottom means: even if a $600 refund matches the first allow rule, the high risk score upgrades the decision to ABSTAIN — held for human review. Two independent controls. Policy says yes. Risk says wait. Risk wins.

That's defense in depth in seven lines of YAML.

Minute 6: Wrap Your Tools

This is the one code change. One import, one decorator per tool:

from faramesh.integrations.langchain import faramesh_tool

@faramesh_tool(agent_id="support-agent-prod")
@tool
def process_refund(customer_id: str, amount: float) -> str:
    """Process a customer refund."""
    return payments_api.refund(customer_id, amount)

@faramesh_tool(agent_id="support-agent-prod")
@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a customer."""
    return email_client.send(to, subject, body)
from faramesh.integrations.langchain import faramesh_tool

@faramesh_tool(agent_id="support-agent-prod")
@tool
def process_refund(customer_id: str, amount: float) -> str:
    """Process a customer refund."""
    return payments_api.refund(customer_id, amount)

@faramesh_tool(agent_id="support-agent-prod")
@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a customer."""
    return email_client.send(to, subject, body)
from faramesh.integrations.langchain import faramesh_tool

@faramesh_tool(agent_id="support-agent-prod")
@tool
def process_refund(customer_id: str, amount: float) -> str:
    """Process a customer refund."""
    return payments_api.refund(customer_id, amount)

@faramesh_tool(agent_id="support-agent-prod")
@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a customer."""
    return email_client.send(to, subject, body)

That's the entire integration. The faramesh_tool decorator intercepts the call before it reaches your function, submits it to the local Faramesh server, waits for the decision, and either proceeds or raises an exception.

Your function doesn't change. Your agent doesn't change. The model doesn't know anything is different. The authorization boundary is transparent to everything above it.

Minute 7: Watch the First Action

Run your agent with a small refund request:

In your terminal, the Faramesh CLI is running in watch mode:

You see this appear in real time:

Green. Permitted. 2.1 milliseconds to evaluate. The refund executes. The customer gets their $45.

Now try a bigger one:




The action is held. The agent does not proceed. The refund does not execute. The customer gets a response saying their request is being reviewed.

Open the web dashboard. The approvals queue has one item. You can see the exact action — customer ID, amount, the policy rule that triggered the hold, the risk score. You approve it. The agent receives the approval, the policy re-evaluates, the refund executes.

Now try the large one:




Red. Denied. The function never ran. The payments API was never called. The agent responds to the customer explaining that large refunds require a different process.

Minute 8: Look at What Got Recorded




Every action. Every decision. Every risk score. The policy version hash that evaluated each one. Whether a human was involved. How long it took.

faramesh actions get
faramesh actions get
faramesh actions get



That record is cryptographically chained to every record before it. You cannot modify it without breaking the chain. You cannot delete it without breaking the chain. If someone asks six months from now "why did this $340 refund execute?" you have an immutable answer: policy version X, human approval by user Y, at timestamp Z, for canonical action with hash H.

That is not a log. That is a Decision Provenance Record. The difference matters when the question is asked under legal pressure rather than engineering curiosity.

Minute 9: Try to Break It

Change the policy to deny all refunds above $50. Save the file. The server picks it up immediately — no restart required, Faramesh watches the policy directory.

Run the $45 refund again.




Wait — the $45 refund was supposed to be allowed. You made a mistake in the policy. But notice what happened: it failed closed. The action was denied, not permitted. There was no matching rule for amounts between $0 and $50 in your new policy, so the default behavior was DENY.

That is not a bug. That is the design. In the Faramesh Core Specification this is called fail-closed semantics:

"If no policy rule matches, the server MUST produce HALT with reason 'default_deny'."

Every other system in your agent stack fails open — if nothing catches it, it proceeds. Faramesh fails closed. If nothing explicitly permits it, it stops.

Fix your policy. The $45 refund works again in 30 seconds.

Minute 10: Add It to LangChain Properly

If you're already using LangChain, the integration is a one-line wrapper on your existing tool list:

from faramesh.integrations.langchain import wrap_tools

# Before
agent = AgentExecutor(agent=llm_agent, tools=[process_refund, send_email])

# After  
governed_tools = wrap_tools(
    [process_refund, send_email],
    agent_id="support-agent-prod",
    policy="policies/support-agent.yaml"
)
agent = AgentExecutor(agent=llm_agent, tools=governed_tools)
from faramesh.integrations.langchain import wrap_tools

# Before
agent = AgentExecutor(agent=llm_agent, tools=[process_refund, send_email])

# After  
governed_tools = wrap_tools(
    [process_refund, send_email],
    agent_id="support-agent-prod",
    policy="policies/support-agent.yaml"
)
agent = AgentExecutor(agent=llm_agent, tools=governed_tools)
from faramesh.integrations.langchain import wrap_tools

# Before
agent = AgentExecutor(agent=llm_agent, tools=[process_refund, send_email])

# After  
governed_tools = wrap_tools(
    [process_refund, send_email],
    agent_id="support-agent-prod",
    policy="policies/support-agent.yaml"
)
agent = AgentExecutor(agent=llm_agent, tools=governed_tools)

Same pattern works for CrewAI, AutoGen, LangGraph, LlamaIndex, and MCP. The wrapper is framework-agnostic — it intercepts at the tool execution layer, not at the framework level. Whatever framework you're using, the governance happens at the same point: between the model's decision and the tool's execution.

Minute 11–12: Docker Compose for Your Team

You don't want to run the server locally forever. To give your whole team access to the dashboard, the approvals queue, and the action history:

docker compose up -d
docker compose up -d
docker compose up -d

The Docker Compose file in the repo starts the Faramesh server, the web UI, and a PostgreSQL database. Point the .env at the Postgres instance instead of SQLite. Your team can now see every action your agent has taken, approve pending actions from the dashboard, and review the full audit history — from anywhere.

For production, Faramesh Horizon is the managed version. Same behavior, no server to run, multi-tenant, with API keys and secrets management through your existing Vault or AWS Secrets Manager.

What Just Happened

Twelve minutes ago your agent had two tools with no authorization layer. Now it has:

A policy that explicitly defines what each tool can do, at what parameter ranges, with what risk classification. Checked into git. Reviewed like code.

An execution gate that evaluates every tool call before it runs, in under 2ms, with fail-closed semantics. Nothing executes without a decision.

A cryptographically chained audit trail that records not just what happened but why it was authorized, under which policy version, with what risk score, and whether a human was involved.

An approval workflow that held a mid-range refund for human review and blocked a large refund entirely — without changing a single line of agent logic or model instruction.

The agent's behavior didn't change. The model doesn't know anything is different. What changed is that you now have a non-bypassable layer that knows the difference between the model deciding to do something and that thing being authorized to happen.

That distinction is what production agents need. And it took twelve minutes to add.

Faramesh Core is open source at github.com/faramesh/faramesh-core.
The full specification is at
arxiv.org/pdf/2601.17744.

Previous

More

Previous

More

Next

More

Next

More

[GET STARTED IN MINUTES]

Ready to give Faramesh a try?

The execution boundary your agents are missing.
Start free. No credit card required.

[GET STARTED IN MINUTES]

Ready to give Faramesh a try?

The execution boundary your agents are missing.
Start free. No credit card required.