Skip to content

About

Put an agent you already run under a KIFF Card: check each tool call with KIFF before it runs. Observe to see what it does, enforce to hold or refuse what falls outside. Python + TypeScript, 11 framework adapters.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

54 Commits

Folders and files

Repository files navigation

kiff-guard

Put an agent you already run under a KIFF Card. kiff-guard is the client SDK and framework adapters that check each of your agent's tool calls with KIFF before the tool runs.

A KIFF Card is the authority a business gives one agent: which actions it may take, how much per action and in total over a window, and what happens to a call outside it (it waits for the owner, or it is refused). The owner changes or revokes the Card without changing the agent. kiff-guard is how an agent in your own code reaches that check.

One guard, two modes:

  • observe runs every tool, records an audit trail, and learns which tools the agent calls. No KIFF account, no domain, no API call. It shows you what the agent actually does, which is what you need to write its Card.
  • enforce asks KIFF to decide before each tool runs, against the domain and the agent's Card. allowed proceeds; anything else (approval_required, blocked, invalid, or any future outcome) withholds the call. Fail-safe by construction.

The same one-line integration also derives a starter KIFF domain from real traffic, so you never start from a blank policy file.

Part of KIFF. This repo is the client SDK and framework adapters, MIT-licensed. The decision engine is the open-source framework at kiff/kiff. Cards, owner approvals and signed receipts are part of KIFF Cloud.

Agent calls MCP tools? You may not need this SDK: point the agent at the KIFF MCP gateway (mcp.kiff.dev) and KIFF checks each call against its Card with no code change. See kiff.dev/docs/connect-an-agent.

Connect path

Connect starts from the agent you already run:

  1. Attach an adapter to the framework's pre-tool-execution seam, or wrap a plain function call with the core Guard.
  2. Observe real tool calls without a KIFF account or domain. The agent still runs; the guard records what tools are being called and learns the action catalog.
  3. Derive starter KIFF domain material from that traffic: candidate actions, entity hints, required parameters, and the runtime/source evidence they came from.
  4. Enforce by pointing the same guard at a KIFF runtime. Every tool call asks for a decision first: allowed runs, anything else withholds. With an API key bound to the agent, the guard proposes as that agent, so the decision draws on the agent's Card: a call over its limit is held for the owner or refused before the tool runs.

The framework repo (kiff/kiff) owns the domain/runtime kernel. This repo owns the connection layer: SDK primitives, framework adapters, observe/enforce wrappers, runtime registration, and source attribution.

Tracking: cloud Connect epic kiff/kiff-cloud#542; source-attribution follow-up #42.

Repository layout

packages/
  python/kiff-guard/   # the Python SDK (shipped): core + 10 adapters
  js/                  # the TypeScript SDK (shipped): core + OpenClaw adapter

The guard is a framework-agnostic core plus thin adapters, one per agent framework, each translating that framework's pre-tool-execution seam into a single call to the core. The guard logic lives once; an adapter adds no governance logic of its own.

Custom or no framework? You don't need an adapter. The core (Guard

  • HTTPClient) governs any tool call directly over plain HTTP — the adapters are just convenience glue. See the "Custom agent? No adapter required" quickstart in packages/python or packages/js, and the raw-HTTP recipe in cookbook/custom-agent-http for stacks the SDKs don't cover (Ruby, Go, shell).

Python SDK

See packages/python/kiff-guard/README.md for install, quickstart, and per-framework usage.

pip install kiff-guard            # core, zero deps
pip install "kiff-guard[agno]"    # + a framework adapter's deps
from kiff_guard import Guard
from kiff_guard.adapters.agno import agno_hook

guard = Guard(mode="observe")     # zero-config audit; no KIFF account
agent = Agent(model=..., tools=[...], tool_hooks=[agno_hook(guard)])

Adapters

Framework Lang Shape Status
Agno py middleware (tool_hooks) shipped
LangGraph / LangChain py middleware (wrap_tool_call) shipped
Hermes (Nous) py vote (pre_tool_call plugin hook) shipped
OpenAI Agents SDK py vote (tool input guardrail) shipped
Google ADK py vote (before_tool_callback) shipped
Pydantic AI py vote (before_tool_execute hook) shipped
Strands Agents py vote (BeforeToolCallEvent) shipped
Haystack Agents py vote (ConfirmationStrategy) shipped
Microsoft Agent Framework py middleware (FunctionMiddleware, async) shipped
OpenClaw ts vote (before_tool_call) shipped (@kiff/kiff-guard/adapters/openclaw; seam + contract verified)
LlamaIndex py middleware (GuardedAgentWorkflow subclass, async) shipped
Custom / no framework any core Guard + HTTPClient / raw HTTP shipped

Two integration shapes: middleware (the guard runs the tool via a handler continuation) and vote / inverted-control (the framework runs the tool; the hook only votes allow/block). Each adapter documents its verified pre-tool-execution seam and block contract in its module docstring.

Only mapped tools are governed

ToolMap is the list of tools KIFF is asked about. A tool that is not bound has no action to propose, so the runtime is never consulted — and since 1.1.0 the guard withholds rather than clearing it:

guard = Guard(mode="enforce", client=HTTPClient(api_key=..., tool_map=tool_map))
# refund_order is bound  -> KIFF decides
# wire_transfer is not   -> invalid: "not bound in the ToolMap; KIFF was not asked"

This is deliberate and it is the safe direction. Before 1.1.0 an unbound tool was cleared with the reason "unmapped; cleared and audited", which meant a binding you forgot became an ungoverned tool with a receipt claiming otherwise.

For a staged rollout where the map is still being filled in, opt back in explicitly — but understand that such a deployment governs only what you remembered to map:

HTTPClient(api_key=..., tool_map=tool_map, unmapped="allow")

Use kiff-scan or kiff scan to find consequential calls you have not bound.

Compatibility

Both SDKs share a version number and are released from the same tag. Semantic versioning applies to the core: Guard, HTTPClient, ToolMap, and the decision shape. Those are what your code imports and what a major version protects.

Adapters are scoped out of that promise. An adapter is glue over another project's interception seam, and agent frameworks change those seams on their own schedule — sometimes in a patch. When an upstream framework moves its hook, the adapter follows it in a minor release rather than forcing a major version for a break we did not introduce. Each adapter pins the framework range it is tested against, and CI runs it against that framework's latest so drift surfaces as a red badge.

In practice: pin the SDK normally, and treat an adapter's framework range as the real compatibility contract for that framework.

Contributing an adapter

Every adapter must pass the conformance suite (kiff_guard.conformance) — a contract that pins the invariants all adapters share (observe is decide-independent and one-receipt; enforce is one-receipt; unknown outcomes fail safe; the trust boundary holds). Add a small drive shim in tests/test_conformance.py and pass it; that's the bar, not a line-by-line audit.

Support tiers: a small set of adapters are maintained tier-1; the rest are community/best-effort. Each adapter pins the framework version range it's tested against, and CI runs against each framework's latest so breakage shows as a red badge, not a silent rot.

TypeScript SDK

See packages/js/README.md for install, quickstart, and usage.

npm install @kiff/kiff-guard
import { Guard } from "@kiff/kiff-guard";
import { registerKiffGuard } from "@kiff/kiff-guard/adapters/openclaw";

const guard = new Guard({ mode: "observe" });  // zero-config audit
// register on OpenClaw plugin api (see package README for full example)

The TypeScript SDK is a faithful port of the Python SDK: same architecture (Guard, Decision, Catalog, Client), same primitives (observe/decideOnly/ recordExecuted/recordWithheld/evaluate), same invariants. Both SDKs speak the same versioned decide contract (/v1, additive-only).

Cookbook

See cookbook/README.md for runnable recipes proving KIFF stops risky agent actions before they execute.

Each recipe is a complete, runnable proof: real models, real agent frameworks, real side effects, real KIFF runtime. Deploy locally or on your own infra, run the scenario, see the verdict.

# Recipe Adapter Verdict
1 duplicate-payment-guard OpenClaw (TS) $100K → $10K, 9 blocked
2 refund-ceiling-guard LangGraph $250 → $100, 3 blocked
3 collections-promise-guard Agno 5 contacts → 1, 4 blocked
4 chargeback-dispute-guard Strands $125 → $25, 4 blocked
5 vulnerability-escalation-guard Agno Teams 6 actions → 1, whole team halted by one event
6 kyb-verification-guard Agno Workflows $60 → $12, 4 blocked (once-and-done)

Recipes 5 and 6 also show framework guardrails PLUS KIFF: Agno's PIIDetectionGuardrail (a pre_hook, input safety) and KIFF (a tool_hook, action authority) running side by side — different layers, not competitors.

Enablement recipes

A second family with the opposite framing: instead of leading with the block, the agent does the work — a real Agno agent performs the legitimate revenue/ops action (KIFF allows it, state advances), then is declined on every repeat once state moves on. The boundary is what makes putting the agent on the task shippable. E2–E5 run on kiff/kiff v0.6.0 (policy-owned roles) + Agno v2.

# Recipe Adapter Verdict
E1 refund-enablement-guard Agno refund issued, 4 repeats declined
E2 deal-close-enablement-guard Agno 1 discount, 4 declined (no stacking)
E3 payment-recovery-enablement-guard Agno 1 charge recovered, 4 declined
E4 instant-payout-enablement-guard Agno 1 payout, 4 declined (one-per-escrow)
E5 prompt-injection-refund-guard Agno adversarial: 0 extra payouts, 2/2 money paths declined

License

MIT. See LICENSE.

About

Put an agent you already run under a KIFF Card: check each tool call with KIFF before it runs. Observe to see what it does, enforce to hold or refuse what falls outside. Python + TypeScript, 11 framework adapters.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages