Solo builders · Indie hackers · Side projects

Approve before your agent breaks something

You're shipping an AI agent that sends emails, writes records, or calls production APIs. You need a review step before it fires. Gate adds that in 10 lines. No infra, no dashboard to build — one command to start.

See it in action → Read the docs GitHub →

3 scenarios where builders need Gate

Scenario 1 — Cold email agent

Your outreach agent drafts and sends personalized emails. It has 800 leads queued. Before it fires a single send, Gate reviews each draft. Content policy blocks anything with banned phrases. Batch sends over 10 require your approval.

Scenario 2 — CRM sync agent

Your agent syncs enriched data back to HubSpot or Salesforce. Gate blocks imports over your threshold. You review and approve the 847-record batch from the dashboard before a single row lands.

Scenario 3 — Agentic product feature

Your product lets users trigger AI actions. Gate sits between the AI decision and the actual API call. Users see "pending review" instead of a silent failure — and you catch the bad writes before they happen.

Start in 2 minutes

# Terminal 1 — start Gate
npx zehrava-gate --port 4000

# Terminal 2 — register your agent
curl -X POST http://localhost:4000/v1/agents/register \
  -H "Content-Type: application/json" \
  -d '{"name":"my-agent","riskTier":"standard"}'
# → { "agentId": "agt_…", "apiKey": "gate_sk_…" }

Replace your direct writes with Gate proposals

# Before — agent writes directly
import requests
requests.post("https://api.sendgrid.com/v3/mail/send", json=email_payload)

# After — agent proposes through Gate
from zehrava_gate import Gate

gate = Gate(endpoint="http://localhost:4000", api_key="gate_sk_...")

p = gate.propose(
    payload=email_body,          # what the agent wants to send
    destination="sendgrid.send", # where it wants to send it
    policy="outbound-email",     # which rules apply
    record_count=1
)

if p["status"] == "approved":
    order = gate.execute(intent_id=p["intentId"])
    # pass order["execution_token"] to your sender worker

elif p["status"] == "pending_approval":
    print(f"Queued for review → http://localhost:4000/dashboard")
    # your agent stops here — write does not happen

elif p["status"] in ["blocked", "duplicate_blocked"]:
    print(f"Blocked: {p['blockReason']}")

Write a 5-line policy

# policies/outbound-email.yaml
id: outbound-email
destinations: [sendgrid.send, gmail.send, loops.send]
block_if_terms:
  - "refund guaranteed"
  - "limited time offer"
auto_approve_under: 1    # single emails auto-approve
require_approval_over: 5 # anything bulk → you review

Review and approve at /dashboard

Every intent lands in the approval queue at http://localhost:4000/dashboard. Approve individually or reject with a reason. The audit trail records every decision with timestamp and actor name.

Gate runs locally during development and inside your VPC in production. No external SaaS. No data leaves your network.


Why not just add an if statement?

The common alternative is a manual review step in the code — log the output, check it, then proceed. Gate gives you 4 things an if statement does not:

  1. Idempotency keys — your retry logic won't send the same email twice
  2. Audit trail — every proposed write, every decision, every actor logged automatically
  3. Policy file — rules live in YAML, not scattered through application code
  4. Dashboard — review interface without building one

Gate is MIT licensed and self-hosted. Run it anywhere Node.js runs.

Star on GitHub → Try the demo Read the docs