Decide what an AI agent may do before it does it.
KIFF is an open-source Go engine that decides each consequential action an AI
agent proposes, before your system runs it. An agent, a person or a service
proposes a named action, such as refunding an order. KIFF checks it against the
current state of that order, the actor's permissions, the parameters, any
approval the action requires, and how much that actor may still do in total. It
returns allowed, approval_required, blocked or invalid, and records the
decision in one replayable history. Your executor runs only what was allowed.
The rules live in an operational domain. Events establish state. State determines which named actions are valid. Permissions, approvals and limits determine who may do what, and how much. Execution results return to the same history, so every outcome can be explained and replayed.
KIFF does not replace your databases, your agent framework or your systems of record. It sits in front of the consequential calls into them.
On kiff.dev, this engine checks an agent's Card: the authority a business gives one agent. A Card says which actions the agent 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.
This repository has the parts a Card is built on: named actions, state,
approvals, and per-actor limits with a ledger
(pkg/kiff/limit). Issuing, sharing and revoking Cards,
the owner answering held calls, a shared ledger, and signed receipts are
features of the hosted service. The table under
Run It Yourself or Hosted says exactly what
differs.
Ask where an agent's limits are written down today and the answer is usually a prompt, a config file, or a tool handler. Nobody responsible for the money can read them, every change is a deploy, and every new agent writes its own copy.
KIFF keeps that authority outside the agent, in one domain that every actor proposes against. The agent can be replaced; the rules for what it may do stay. Every actor gets the same answers:
| Question | KIFF's answer |
|---|---|
| What happened? | Recorded events and execution outcomes |
| What is true now? | State derived from those events |
| What may happen next? | Named actions and valid transitions |
| Who may do it, and how much? | Permissions, approvals, and limits |
| Why did it happen? | One decision and audit history |
The first agent is usually a feature. The next few need a system.
When three actors join the same process, the common pattern is three backend copies. Each owns an interpretation of state, rules, actions, and a custom path into the same systems. That works in isolation. Together, those copies drift, disagree, and multiply the coordination work.
The actors and systems do not change. KIFF replaces the repeated domain copies with one operational domain and one validated execution path. Every actor sees the same state, proposes the same named actions, and meets the same authority, approval, and limit rules.
This does not replace your agent framework, HTTP stack, queue, cron job, or systems of record. It evaluates proposed actions against current state before your executor runs.
| Without KIFF | With KIFF | Practical effect |
|---|---|---|
| Each actor carries its own version of state and rules. | Events and derived state are shared. | Actors stop disagreeing about what is true. |
| Every new actor builds another operational backend. | Actors propose the same named actions. | Add agents without rebuilding the operation. |
| Decisions and limits live in application code, prompts, and tool handlers. | One decision point evaluates every proposal. | Consistent approvals, limits, and refusals. |
| Logs are scattered across services. | Events, decisions, and results form one history. | Replay and explain an operational outcome. |
One domain, one refund:
order-2 is PAID
agent → REFUND_ORDER $999, high-risk
KIFF ⏸ HELD — approval required the executor did NOT run
human → approves
KIFF ✓ ALLOWED — refund executes → REFUNDED
agent → REFUND_ORDER (same call, retried)
KIFF ✗ BLOCKED — order is already REFUNDED refused before money moves again
replay from events alone → REFUNDED materialized == replayed
The useful action runs. The risky one waits for a human. The path rebuilds from the event log.
Be clear about that third line, because it is the one most likely to be oversold. Refusing a repeat on an already-refunded order is table stakes: a unique constraint does it, and whatever framework handed your agent the refund tool very likely does it too. It is shown here because it is the boundary reading state, not because it is a reason to adopt one.
Your agent is probably already bounded. Whatever gave it the refund tool scoped what it may call, from which state, with which parameters — and it is right every time it runs. It runs once per call, so ten correct refunds is ten correct decisions.
a limit: refund-agent may refund €1,100 per calendar day
agent → AUTO_REFUND €42 ✓ ALLOWED €1,058 left
agent → AUTO_REFUND €990 ✓ ALLOWED €68 left
agent → AUTO_REFUND €88 ✗ REFUSED limit_reached
the third is in the right state, with the right permission, valid
parameters, under the approval threshold, and smaller than the two that
just went through. Nothing is wrong with it. The day is spent.
That refusal cannot come from checking one call, because no single call can see
the other two. pkg/kiff/limit holds the ledger that can.
It is the same check behind a KIFF Card.
rt, err := runtime.New(runtime.Config{
Domain: &definition,
Limits: runtime.StaticLimits{{
ID: "refund-agent-daily",
Subject: "refund-agent",
Actions: []string{"AUTO_REFUND"},
Aggregates: []limit.Aggregate{{
Quantity: limit.Quantity{Parameter: "amount"},
Max: 110000, // minor units
Window: limit.WindowCalendarDay,
}},
}},
LimitLedger: limit.NewMemoryLedger(),
})Four properties are worth knowing before you rely on it:
- A limit constrains; it never confers. An actor with no limit is unaffected, so a first limit cannot break a running system.
- A revoked limit refuses. It does not vanish and leave the actor unbounded. The obvious implementation filters to the limits in force and reads the empty set as "nothing applies, so allow" — which makes revoking a limit the act that removes the bound.
- An unreadable ledger refuses. Allowing on an unknown balance is the thing a limit exists to prevent.
MemoryLedgeris correct for one process, and only that. Two replicas each hold their own ledger and each enforces the full ceiling, so the real total is the sum. Implementlimit.Ledgeragainst shared storage when you run more than one — a bound on a sequence has to live where the whole sequence is visible.
Run the live tour:
go run ./cmd/kiff-tourgo install github.com/kiff/kiff/cmd/kiff@latest
kiff new -scenario refund github.com/acme/refunds
cd refunds
go mod tidy
make demoThat creates a runnable refund domain with an Order, a MARK_PAID action, an
approval-gated REFUND_ORDER action, a headless HTTP API, and a demo script.
Use kiff new <module> without -scenario for a smaller starter project.
The kiff CLI stays with the project from the first domain to a running
system:
| Workflow | What it gives you | Commands |
|---|---|---|
| Start | A runnable KIFF project, with an optional working scenario | kiff new |
| Generate | A typed Go domain from a JSON descriptor | kiff scaffold |
| Validate | Structural domain checks and static analysis for unguarded Go tools | kiff verify, kiff scan |
| Investigate | A readable action, approval, and outcome history for one entity | kiff timeline |
| Publish | Authentication and domain contract deployment to KIFF Cloud | kiff auth, kiff apply |
| Operate | Views of domains, runtimes, governed usage, and active API keys | kiff domains, kiff runtimes, kiff usage, kiff keys |
Run kiff help for the full command list or kiff <command> -h for flags.
If you already have an agent, you can put its tool calls in front of KIFF without restructuring it. The guard SDK connects KIFF to the pre-execution seam your framework already exposes:
pip install kiff-guard # or: npm install @kiff/kiff-guardfrom kiff_guard import Guard
from kiff_guard.adapters.agno import agno_hook
guard = Guard(mode="observe") # observe records; enforce refuses before the call runs
agent = Agent(model=..., tools=[refund_order], tool_hooks=[agno_hook(guard)])KIFF Guard ships 11 framework adapters across Python and TypeScript:
| Agno Python adapter |
LangGraph / LangChain Python adapter |
OpenAI Agents SDK Python adapter |
| Google ADK Python adapter |
Pydantic AI Python adapter |
Strands Agents Python adapter |
| Microsoft Agent Framework Python adapter |
Hermes Python adapter |
Haystack Agents Python adapter |
| LlamaIndex Python adapter |
OpenClaw TypeScript adapter |
Custom / no framework No adapter required |
The adapters are thin integrations at each framework's pre-tool-call boundary; KIFF's decision logic stays in the shared guard core. A custom stack can call that core directly or use plain HTTP without an adapter. See the SDK quickstarts and adapter docs (MIT).
The framework and guard SDK are MIT. Self-host your operational domain with the
postgres store and your own HTTP stack for as long as you like.
If you would rather not operate the state, approvals, receipts, and retention
yourself, KIFF Cloud runs them as a hosted control plane
with a free tier. The CLI talks to it directly: kiff auth login, then
kiff apply to push a domain contract, and kiff domains, kiff runtimes,
kiff usage, kiff keys to inspect a tenant. Nothing in this repository
requires it.
Worth stating plainly, because the two are not in lockstep and a gap here is not always a gap there. This repository is the boundary; the hosted runtime was pushed harder to get something real in front of users, and parts of it have not been folded back.
| This repository | Hosted | |
|---|---|---|
| Decision boundary, approvals, replay | yes | same code |
| Aggregate limits | yes (in-process ledger) | issued as KIFF Cards: shared ledger, statements, Cards shared by several agents, owner approval of held calls, and a management credential separating who may act from who sets how much |
| Audit records | append-only, no hash, no signature | each record's data hashed into a causal chain, the chain signed, an hourly root committed to Base Sepolia (a testnet: signed and verifiable, not a settlement guarantee) |
| Tenancy, billing, retention | not attempted | the product |
| Lease between a decision and the executor | open | also open |
The last row is the one worth reading. Where the hosted runtime is ahead it is said above; where it is not, saying so is the only way this table stays worth consulting.
| Capability | What KIFF provides |
|---|---|
| Shared state | Events, state transitions, replay, and memory, file, or postgres stores |
| Named actions | Typed parameters, allowed states, required permissions, risk, and explicit executors |
| Human authority | Dynamic approvals, and segregation of duties enforced by default — the actor that requested an approval cannot review it. Reviewer permissions are opt-in per contract, because only the domain knows which permission is sufficient |
| Reliable execution | Validation before side effects and idempotency protection for consequential retries |
| Explainable history | Proposals, decisions, approvals, execution results, and failures as durable protocol data. Records are append-only by construction; tamper-evidence (hash-chained, signed receipts) is a KIFF Cloud feature, not a property of the OSS stores |
| Open interfaces | An optional net/http API and CLI for agents, services, operators, and CI. The API authenticates through a pluggable Authenticator, and the principal it establishes overrides any actor in the request body |
Use kiff scan . inside a Go agent repository to find explicit agent tools
that can reach consequential operations without a recognized decision earlier
in the function. Mark a handler with //kiff:tool, pass it to a common tool
registration call, or identify it with -tool FunctionName. The initial Go
scanner emits terminal, JSON, and SARIF reports and can enforce a CI threshold:
kiff scan .
kiff scan -format sarif -output kiff-scan.sarif -fail-on high .This is static analysis: a clean result means no supported path was found, not that the application is safe or that every framework-specific registration shape is understood.
For Python agent repositories, use the separate kiff-scan tool instead:
uvx kiff-scan scan .It also ships a GitHub Action: uses: kiff/kiff-scan@v0.1.1.
These integrations help developers run KIFF's scanners, understand findings, and fix the code. They are development tools, not runtime agent adapters.
| Codex Agent Skill |
Claude Code Plugin / skill |
Kiro Power / Agent Plugin |
All three ship the same two skills. See the installation guide for setup and example prompts.
kiff-governance— run the scanners, explain findings, and route consequential actions through a real decision boundary. Development workflow.kiff-audit— an adversarial governance audit: extract the guarantees a codebase claims, map its trust boundary, construct and execute attacks against those guarantees, run the language's test and vulnerability tooling, and produce an evidence-backed report with P0/P1 findings and a verdict.
kiff-audit is deliberately not a linter. It treats a claimed guarantee as false
until an attack against it has failed, and it reports what it could not establish
as not assessable rather than as a pass. The
reference audit is a real
engagement against this repository in which three of four attacks on the
self-approval boundary succeeded.
- Why KIFF — why agents, people, and services need one domain to act against
- The governed action boundary — how decisions, approvals, and replay work
- The side-effect boundary — deployment topology: agents propose, executors own credentials
- Limits — what an actor may do in total, and why no per-call check can answer it
- Cookbook guide — choose, evaluate, and adapt a governed agent recipe
- Build a domain — the authoring guide, end to end
- Scaffold from a descriptor — generate a domain from JSON
- Govern over HTTP — drive KIFF from TypeScript, Python, or any stack
- Coding assistant integrations — install and use the Codex, Claude Code, and Kiro skill
- Architecture & packages — the package map and responsibilities
- Philosophy and Comparisons — what KIFF is, and where it stops
- examples/refund — one entity, three states, two actions
- examples/mission — a larger stateful coordination domain
- examples/llm-bridge — the tool-call bridge pattern
Use the cookbook when you want to see what KIFF lets a team launch, not just what it can audit after the fact. These recipes model agents proposing useful work while KIFF owns the consequential action boundary.
- accounts-payable-payout — a Claude Haiku AP agent with a money-moving payout boundary, finance approval, and lifecycle view
- security-incident-response — containment decisions, session reset, and access revocation through an identity-service boundary
- procurement-purchase-order — purchase-order creation through an ERP service with manager approval
- insurance-claims-triage — claim evidence, coverage/risk scoring, and payout execution
- healthcare-prior-auth — clinical documentation, payer criteria, and portal submission
- cloud-infra-remediation — infrastructure remediation with approval-gated isolation
- vendor-bank-change — vendor payment-detail changes with finance-controlled execution
- cookbook index — recipe standards, feature map, and later candidates
If your app is simple CRUD, or direct LLM tool calls with no consequential state, KIFF is too much structure — ship something smaller. KIFF earns its keep when agents take consequential actions, what they may do depends on current state and on how much they have already done, some actions need a human sign-off, and someone eventually asks "why did this happen?"
KIFF is at v0.9. The release adds limits — a ceiling on what one actor may do
in total, which is the one check a per-call boundary cannot make — plus an
adversarial governance audit skill, an authoritative state machine, an
authenticated HTTP API, and segregation of duties by default. The core
action boundary is complete and adversarially tested: the approved bit cannot
be forged from outside the module — including by reflection or unsafe, which
a compile-time boundary cannot prevent — a pluggable validator cannot waive a
required approval, the state machine is authoritative for validation rather
than the caller, the HTTP API authenticates and the principal overrides any
actor in the request body, and the actor that requested an approval cannot
review it. Those attacks ship as conformance tests that fail the build if the
boundary regresses.
Known gaps, stated plainly: audit records are durable and append-only but not tamper-evident in the OSS stores; there is no lease or version check between a decision and the executor, so a decision can in principle be acted on against state that has since moved; and the bundled limit ledger is in-process, so two replicas each enforce the full ceiling and the real total is the sum. The Postgres store is the production reference; the file-backed JSONL stores are for demos and local development.
MIT. Use it. Fork it. Ship with it.


