Integration Guide
This guide walks you through connecting your existing AI agent to TameFlare. TameFlare supports two integration modes:
- Proxy enforcement (v2) — recommended. Zero code changes. The proxy intercepts all HTTP traffic.
- SDK advisory (v1) — the agent voluntarily checks with TameFlare before acting.
Proxy Integration (v2, recommended)
The proxy model requires no code changes to your agent. It works with any language or framework.
1. Initialize and add connectors
npx tf init
npx tf connector add github --token ghp_xxx
npx tf connector add openai --token sk-xxxOr use a platform template:
npx tf init --platform langchain # Pre-configured for LLM agents
npx tf init --platform n8n # Pre-configured for workflow automation
npx tf init --platform claude-code # Pre-configured for coding assistants2. Set permissions
npx tf permissions set --gateway "Bot" --connector github \
--action "github.pr.*" --decision allow
npx tf permissions set --gateway "Bot" --connector github \
--action "github.pr.merge" --decision require_approval
npx tf permissions set --gateway "Bot" --connector github \
--action "github.repo.delete" --decision deny3. Run your agent
npx tf run --name "Bot" python my_agent.py
npx tf run --name "Bot" node my_agent.js
npx tf run --name "Bot" ./my_agentThat's it. All outbound HTTP traffic is now proxied. The agent never sees real API keys.
4. Monitor
npx tf status # Gateway + agent stats
npx tf logs # Live traffic log
npx tf approvals list # Pending approvals
npx tf approvals approve <id> # Approve a held requestOr use the dashboard at http://localhost:3000/dashboard.
Available connectors
| Connector | Domains | Actions |
|---|---|---|
| github | api.github.com | 20+ (PRs, issues, branches, releases, files, repos) |
| openai | api.openai.com | 24+ (chat, embeddings, images, audio, files, fine-tuning) |
| anthropic | api.anthropic.com | 2+ (messages, models) |
| stripe | api.stripe.com | 40+ (charges, refunds, payments, subscriptions, invoices) |
| slack | slack.com, api.slack.com | 35+ (messages, channels, files, users, admin) |
| generic | Any domain | Method-based (GET, POST, PUT, DELETE) |
Why proxy mode is better
- Zero code changes — works with any agent, any language
- Deny-all default — no connector = no access
- Credential isolation — agent never sees real API keys
- Hold-connection approvals — proxy holds HTTP request until human approves
- Cannot be bypassed — agent's traffic is physically routed through the proxy
SDK Integration (v1, advisory mode)
The SDK mode is useful when you want the agent to voluntarily check with TameFlare before acting. The agent keeps its own credentials.
Before you start
You need a running TameFlare instance and at least one registered agent:
- Follow the Quick Start to get tf running
- Go to Dashboard > Agents and click Register agent to get an API key
- Install at least one policy (the GitHub Safe Defaults pack is a good start)
You'll need two values:
| Value | Where to find it |
|---|---|
| AAF_API_KEY | Shown once when you register an agent (e.g. aaf_test_abc123...) |
| AAF_BASE_URL | Your TameFlare instance URL + /api (e.g. http://localhost:3000/api) |
Node.js agent
1. Install the SDK
# Install from source (npm package coming soon)
npm install ./packages/sdk-node2. Initialize the client
import { AgentFirewall } from "@agentfirewall/sdk";
const TameFlare = new AgentFirewall({
apiKey: process.env.AAF_API_KEY,
baseUrl: process.env.AAF_BASE_URL ?? "http://localhost:3000/api",
});3. Wrap your agent's actions
Before TameFlare — your agent calls GitHub directly:
// OLD: Agent calls GitHub directly
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
await octokit.pulls.merge({ owner: "acme", repo: "api", pull_number: 42 });After TameFlare (Mode A) — agent requests permission, Gateway executes:
// NEW: Agent requests permission, Gateway executes
const result = await TameFlare.requestAction({
type: "github.pr.merge",
resource: {
provider: "github",
account: "acme",
target: "acme/api",
environment: "production",
},
parameters: { pull_number: 42 },
});
switch (result.status) {
case "allowed":
await TameFlare.execute(result.action_request_id, result.decision_token);
break;
case "pending_approval":
const approved = await TameFlare.waitForApproval(result.action_request_id);
if (approved) await TameFlare.execute(result.action_request_id, approved.decision_token);
break;
case "denied":
console.log("Blocked:", result.decision.reason);
break;
}After TameFlare (Mode B) — agent checks policy, then executes itself:
// NEW: Agent checks policy, then executes itself
const result = await TameFlare.requestAction({
type: "github.pr.merge",
resource: {
provider: "github",
account: "acme",
target: "acme/api",
environment: "production",
},
parameters: { pull_number: 42 },
});
if (result.status === "allowed") {
// Agent still holds credentials and executes directly
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
await octokit.pulls.merge({ owner: "acme", repo: "api", pull_number: 42 });
} else if (result.status === "denied") {
console.log("Policy blocked this action:", result.decision.reason);
}Python agent
1. Install the SDK
# Install from source (PyPI package coming soon)
pip install ./packages/sdk-python2. Initialize the client
from agentfirewall import AgentFirewall
import os
TameFlare = AgentFirewall(
api_key=os.environ["AAF_API_KEY"],
base_url=os.environ.get("AAF_BASE_URL", "http://localhost:3000/api"),
)3. Wrap your agent's actions
Before TameFlare:
# OLD: Agent calls Stripe directly
import stripe
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
stripe.Transfer.create(amount=25000, currency="usd", destination="acct_xyz")After TameFlare (Mode A):
# NEW: Agent requests permission, Gateway executes
result = TameFlare.request_action(
action_type="payment.transfer.initiate",
provider="stripe",
account="acct_main",
target="transfers",
environment="production",
parameters={"amount": 25000, "currency": "usd", "destination": "acct_xyz"},
risk_hints={"financial_impact": True, "irreversible": True},
)
if result.status == "allowed":
TameFlare.execute(result.action_request_id, result.decision_token)
elif result.status == "pending_approval":
approved = TameFlare.wait_for_approval(result.action_request_id, timeout_seconds=300)
if approved:
TameFlare.execute(result.action_request_id, approved.decision_token)
elif result.status == "denied":
print(f"Blocked: {result.decision.reason}")After TameFlare (Mode B):
# NEW: Agent checks policy, then executes itself
result = TameFlare.request_action(
action_type="payment.transfer.initiate",
provider="stripe",
account="acct_main",
target="transfers",
environment="production",
parameters={"amount": 25000, "currency": "usd", "destination": "acct_xyz"},
)
if result.status == "allowed":
# Agent still holds credentials
stripe.Transfer.create(amount=25000, currency="usd", destination="acct_xyz")
elif result.status == "denied":
print(f"Blocked: {result.decision.reason}")LangChain
LangChain agents use tools to interact with external services. Wrap each tool with an TameFlare check.
Wrapping a LangChain tool
from langchain.tools import tool
from agentfirewall import AgentFirewall
TameFlare = AgentFirewall(api_key=os.environ["AAF_API_KEY"])
@tool
def create_github_issue(repo: str, title: str, body: str) -> str:
"""Create a GitHub issue. Checked by TameFlare policy engine."""
result = TameFlare.request_action(
action_type="github.issue.create",
provider="github",
account=repo.split("/")[0],
target=repo,
environment="production",
parameters={"title": title, "body": body},
)
if result.status == "allowed":
exec_result = TameFlare.execute(result.action_request_id, result.decision_token)
return f"Issue created: {exec_result}"
elif result.status == "denied":
return f"Action blocked by policy: {result.decision.reason}"
elif result.status == "pending_approval":
return "Action requires human approval. Waiting..."Using with an agent
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
tools = [create_github_issue] # TameFlare-wrapped tools
agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
result = executor.invoke({"input": "Create an issue about the login bug"})CrewAI
CrewAI agents use tools similarly to LangChain. Wrap each tool with an TameFlare check.
from crewai import Agent, Task, Crew
from crewai.tools import tool
from agentfirewall import AgentFirewall
TameFlare = AgentFirewall(api_key=os.environ["AAF_API_KEY"])
@tool("Send Email")
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email. Checked by TameFlare policy engine."""
result = TameFlare.request_action(
action_type="email.send",
provider="sendgrid",
account="main",
target=to,
environment="production",
parameters={"to": to, "subject": subject, "body": body},
risk_hints={"external_recipient": not to.endswith("@yourcompany.com")},
)
if result.status == "allowed":
exec_result = TameFlare.execute(result.action_request_id, result.decision_token)
return f"Email sent: {exec_result}"
elif result.status == "denied":
return f"Blocked by policy: {result.decision.reason}"
elif result.status == "pending_approval":
approved = TameFlare.wait_for_approval(result.action_request_id, timeout_seconds=120)
if approved:
TameFlare.execute(result.action_request_id, approved.decision_token)
return "Email sent after approval"
return "Email blocked — approval denied or timed out"
agent = Agent(
role="Communications Manager",
goal="Send project updates to stakeholders",
tools=[send_email],
)Raw HTTP
If you're not using an SDK, you can call the TameFlare API directly.
Request an action
curl -X POST http://localhost:3000/api/v1/actions \
-H "Authorization: Bearer $AAF_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action_spec": {
"type": "github.pr.merge",
"resource": {
"provider": "github",
"account": "acme",
"target": "acme/api",
"environment": "production"
},
"parameters": { "pull_number": 42 }
}
}'Check the response
{
"action_request_id": "act_abc123",
"status": "allowed",
"decision": {
"outcome": "allow",
"reason": "No deny rules matched",
"risk_score": 0.3,
"matched_policies": ["github-safe-defaults"]
},
"decision_token": "eyJhbGciOiJFUzI1NiIs..."
}Execute (Mode A only)
curl -X POST http://localhost:3000/api/v1/actions/act_abc123/execute \
-H "Authorization: Bearer $AAF_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "decision_token": "eyJhbGciOiJFUzI1NiIs..." }'Migrating credentials to the Gateway
To move from Mode B (policy-only) to Mode A (full enforcement):
Step 1: Start the Gateway
cd apps/gateway && go run ./cmd/gatewayOr via Docker:
docker compose up gatewayStep 2: Move credentials
Remove external API credentials from your agent's environment and add them to the Gateway:
| Credential | Move from | Move to |
|---|---|---|
| GitHub PAT | Agent's GITHUB_TOKEN | Dashboard > Settings > Integrations > GitHub PAT |
| Slack token | Agent's SLACK_TOKEN | Dashboard > Settings > Integrations > Slack Bot Token |
| Any HTTP API key | Agent's env vars | Gateway's env or use the webhook connector |
SETTINGS_ENCRYPTION_KEY is configured. Always set this in production.Step 3: Configure the Gateway URL
In the dashboard, go to Settings > Integrations and set the Gateway URL (default: http://localhost:8443).
Step 4: Switch your agent to Mode A
Replace direct API calls with TameFlare.execute() calls. The Gateway will use the credentials you moved in Step 2.
Step 5: Verify
- Run a test action from the dashboard sandbox
- Check the Connectors page — it should show "Gateway online" with your connectors listed
- Check the Audit Log for
action.executedevents
Using the webhook connector for any API
If the Gateway doesn't have a built-in connector for your tool, use the webhook connector. It sends HTTP requests to any URL:
{
"action_spec": {
"type": "webhook.post",
"resource": {
"provider": "webhook",
"target": "https://api.example.com/v1/action"
},
"parameters": {
"method": "POST",
"headers": { "Authorization": "Bearer secret" },
"body": { "key": "value" }
}
}
}Recommended rollout
Proxy mode (v2)
| Step | Action | Enforcement |
|---|---|---|
| 1 | tf init, add connectors, set permissions | — |
| 2 | tf run with monitor enforcement | All traffic forwarded, decisions logged |
| 3 | Review traffic log for 1-2 weeks, tune permissions | Still monitor |
| 4 | Switch to soft_enforce | Denies logged as would_deny, still forwarded |
| 5 | Switch to full_enforce | Denied requests return 403 |
SDK mode (v1)
| Step | Action | Enforcement | |---|---|---| | 1 | Install TameFlare, register agent, install policy packs | — | | 2 | Integrate SDK, set L0 (Monitor) | Agent checks TameFlare but all actions allowed | | 3 | Review audit log, tune policies | Still L0 | | 4 | Switch to L1 (Soft enforce) | Denies logged as warnings | | 5 | Switch to L2 (Full enforce) | All decisions enforced |
How framework interception works
TameFlare intercepts HTTP traffic at the transport layer, not the framework layer. This means it works with any agent framework without plugins or adapters.
Why this matters
| Approach | How it works | Limitation |
|---|---|---|
| Framework plugin | Hook into LangChain/CrewAI tool system | Only works with that framework. Requires code changes. |
| SDK wrapper | Wrap each tool call with an TameFlare check | Requires code changes per tool. Agent can skip the check. |
| Transport interception (TameFlare proxy) | Set HTTP_PROXY env var. All HTTP calls go through the proxy. | Works with everything. Cannot be bypassed by the agent. |
When you run tf run --name "Bot" python my_agent.py, TameFlare sets HTTP_PROXY and HTTPS_PROXY for the child process. Every HTTP library (Python requests, httpx, urllib3, Node.js fetch, axios, Go net/http) respects these variables automatically.
The agent's code doesn't change. The framework doesn't need to know about TameFlare. The proxy sees the raw HTTP request, matches it to a connector, parses the action type, checks permissions, and either forwards (with injected credentials) or blocks.
Framework-specific notes
| Framework | Proxy support | Notes |
|---|---|---|
| LangChain | Automatic | Uses requests/httpx which respect HTTP_PROXY |
| CrewAI | Automatic | Uses requests under the hood |
| n8n | Automatic | Node.js fetch/axios respect proxy env vars |
| OpenClaw | Automatic | Standard HTTP client |
| Claude Code | Automatic | Uses Node.js HTTP client |
| Custom Python | Automatic | requests, httpx, urllib3 all respect proxy |
| Custom Node.js | Automatic | fetch, axios, node-fetch all respect proxy |
| Custom Go | Automatic | net/http respects HTTP_PROXY by default |
| Shell scripts (curl) | Automatic | curl respects proxy env vars |
Multi-agent setup
Run multiple agents through the same gateway with different permissions.
Example: DevOps Bot + Finance Bot
# Add connectors (shared across agents)
npx tf connector add github --token-env GITHUB_TOKEN
npx tf connector add generic --domains api.stripe.com --token-env STRIPE_KEY
# DevOps Bot: full GitHub access, no Stripe
npx tf permissions set --gateway "DevOps Bot" --connector github \
--action "github.*" --decision allow
npx tf permissions set --gateway "DevOps Bot" --connector github \
--action "github.repo.delete" --decision deny
# Finance Bot: Stripe read-only, no GitHub
npx tf permissions set --gateway "Finance Bot" --connector generic \
--action "generic.get" --decision allow
npx tf permissions set --gateway "Finance Bot" --connector generic \
--action "generic.post" --decision require_approval
# Run each agent (each gets a dedicated proxy port)
npx tf run --name "DevOps Bot" python devops_agent.py &
npx tf run --name "Finance Bot" python finance_agent.py &How agent isolation works
- Each agent gets a dedicated proxy port allocated by the gateway
- An agent can only see its own traffic logs and permissions
- Agents cannot access each other's credentials or traffic
- The dashboard shows all agents' activity (scoped by RBAC role)
- The kill switch can target a specific agent:
tf kill-switch --activate --scope "Finance Bot"
Naming conventions
Agent names are free-form strings. Recommendations:
- Use descriptive names:
"DevOps Bot","PR Review Agent","Invoice Processor" - Names are case-sensitive:
"Bot"and"bot"are different agents - Names appear in audit logs, traffic logs, and approval requests
What to do when things go wrong
| Problem | Solution |
|---|---|
| Agent traffic not proxied | Ensure you used tf run — it sets HTTP_PROXY/HTTPS_PROXY env vars |
| Agent gets 403 from proxy | Check tf logs for the denied action. Add a permission rule or switch to monitor mode |
| Approval never resolves | Check tf approvals list. Approvals expire after 5 minutes |
| Kill switch blocking everything | Run tf kill-switch deactivate or check dashboard Settings |
| Agent gets 401 Unauthorized (SDK) | Check AAF_API_KEY is correct and the agent is active |
| Agent gets 429 Too Many Requests (SDK) | Rate limit hit (60 req/min). The SDK auto-retries |
| Gateway shows "offline" | Ensure gateway is running (tf status) or check the Gateway URL in Settings |
| Approval never arrives in Slack | Check Slack integration in Settings (bot token, signing secret, channel ID) |
Next steps
- Writing Policies — create custom rules for your domain
- Core Concepts — understand decisions, tokens, and enforcement levels
- API Reference — full endpoint documentation
- SDKs & CLI — detailed SDK methods and CLI commands